summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2014-09-05 20:15:08 -0700
committerTor Norbye <tnorbye@google.com>2014-09-08 11:58:21 -0700
commit145ec85b4790ed9ade912d24754b38588998dee1 (patch)
tree525f975af50498b0319435e03bf52d55c9a0a1c3
parent2250e7e055295bee73366d360d15ea0270573ef6 (diff)
downloadidea-145ec85b4790ed9ade912d24754b38588998dee1.tar.gz
Don't suggest making activities/fragments private
If you run Analyze > Inspect Code... on a default Android project, you'll end up with suggestions that you should make your various activities and services package protected (since they may not be referenced from other code in the project). However, you can't do that - at runtime the various inflaters will throw exceptions if you do. This CL adds knowledge to the class analyzer in IntelliJ such that it's aware of the Android context classes and does not suggest reducing visibility of these. Change-Id: Ib5e7004dba8b66b6541be87f121195453568822a Conflicts: java/java-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java
-rw-r--r--java/java-analysis-api/src/com/intellij/codeInspection/reference/RefClass.java3
-rw-r--r--java/java-analysis-api/src/com/intellij/codeInspection/reference/RefJavaManager.java13
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java34
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java85
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java5
5 files changed, 136 insertions, 4 deletions
diff --git a/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefClass.java b/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefClass.java
index 3e9044ec5eaf..856285a7bfce 100644
--- a/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefClass.java
+++ b/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefClass.java
@@ -62,6 +62,9 @@ public interface RefClass extends RefJavaElement {
boolean isTestCase();
+ /** Returns true if this class extends one of the Android framework classes that must be public */
+ boolean isAndroidPublic();
+
boolean isLocalClass();
boolean isSelfInheritor(PsiClass psiClass);
diff --git a/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefJavaManager.java b/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefJavaManager.java
index 51ea666f59fa..9ce3a61b5222 100644
--- a/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefJavaManager.java
+++ b/java/java-analysis-api/src/com/intellij/codeInspection/reference/RefJavaManager.java
@@ -71,6 +71,19 @@ public abstract class RefJavaManager implements RefManagerExtension<RefJavaManag
public abstract PsiClass getServlet();
+ // Android Framework APIs that apps extend and where the subclasses must be public
+ // such that the framework can instantiate them
+
+ public abstract PsiClass getAndroidView();
+ public abstract PsiClass getAndroidActivity();
+ public abstract PsiClass getAndroidService();
+ public abstract PsiClass getAndroidBackupAgent();
+ public abstract PsiClass getAndroidContentProvider();
+ public abstract PsiClass getAndroidReceiver();
+ public abstract PsiClass getAndroidFragment(boolean support);
+ public abstract PsiClass getAndroidActionProvider();
+ public abstract PsiClass getAndroidParcelable();
+
public abstract EntryPointsManager getEntryPointsManager();
@NotNull
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java
index 13a18f075aa4..ddaeeb7581b5 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefClassImpl.java
@@ -56,6 +56,7 @@ public class RefClassImpl extends RefJavaElementImpl implements RefClass {
private static final int IS_SERVLET_MASK = 0x400000;
private static final int IS_TESTCASE_MASK = 0x800000;
private static final int IS_LOCAL_MASK = 0x1000000;
+ private static final int IS_ANDROID_MASK = 0x2000000;
private Set<RefClass> myBases; // singleton (to conserve the memory) or THashSet
private Set<RefClass> mySubClasses; // singleton (to conserve the memory) or THashSet
@@ -139,6 +140,26 @@ public class RefClassImpl extends RefJavaElementImpl implements RefClass {
}
}
+ // The Android framework has a number of classes that it wants to
+ // instantiate so it requires these classes to be public, even if
+ // code analysis suggests that that these are only referenced from
+ // within the same package. Unfortunately these do not all extend the
+ // same set of base classes, so we need to check all these cases
+ // independently.
+ RefJavaManager refManager = getRefJavaManager();
+ if (inheritsFrom(psiClass, refManager.getAndroidActivity())
+ || inheritsFrom(psiClass, refManager.getAndroidService())
+ || inheritsFrom(psiClass, refManager.getAndroidView())
+ || inheritsFrom(psiClass, refManager.getAndroidFragment(false))
+ || inheritsFrom(psiClass, refManager.getAndroidFragment(true))
+ || inheritsFrom(psiClass, refManager.getAndroidReceiver())
+ || inheritsFrom(psiClass, refManager.getAndroidContentProvider())
+ || inheritsFrom(psiClass, refManager.getAndroidParcelable())
+ || inheritsFrom(psiClass, refManager.getAndroidBackupAgent())
+ || inheritsFrom(psiClass, refManager.getAndroidActionProvider())) {
+ setAndroidPublic(true);
+ }
+
for (PsiMethod psiMethod : psiMethods) {
RefMethod refMethod = (RefMethod)getRefManager().getReference(psiMethod);
@@ -187,6 +208,10 @@ public class RefClassImpl extends RefJavaElementImpl implements RefClass {
}
}
+ private static boolean inheritsFrom(@NotNull PsiClass c1, @Nullable PsiClass c2) {
+ return c2 != null && c1.isInheritor(c2, true);
+ }
+
private static ServerPageFile getJspFile(PsiClass psiClass) {
final PsiFile psiFile = PsiUtilCore.getTemplateLanguageFile(psiClass);
return psiFile instanceof ServerPageFile ? (ServerPageFile)psiFile : null;
@@ -491,6 +516,11 @@ public class RefClassImpl extends RefJavaElementImpl implements RefClass {
}
@Override
+ public boolean isAndroidPublic() {
+ return checkFlag(IS_ANDROID_MASK);
+ }
+
+ @Override
public boolean isTestCase() {
return checkFlag(IS_TESTCASE_MASK);
}
@@ -557,6 +587,10 @@ public class RefClassImpl extends RefJavaElementImpl implements RefClass {
setFlag(servlet, IS_SERVLET_MASK);
}
+ private void setAndroidPublic(boolean android) {
+ setFlag(android, IS_ANDROID_MASK);
+ }
+
private void setTestCase(boolean testCase) {
setFlag(testCase, IS_TESTCASE_MASK);
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java
index d9291660f9be..341f44a2fdf3 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaManagerImpl.java
@@ -46,6 +46,16 @@ public class RefJavaManagerImpl extends RefJavaManager {
private PsiMethod myAppAgentmainPattern;
private PsiClass myApplet;
private PsiClass myServlet;
+ private PsiClass myAndroidActivity;
+ private PsiClass myAndroidService;
+ private PsiClass myAndroidBackupAgent;
+ private PsiClass myAndroidFragment;
+ private PsiClass myAndroidV4Fragment;
+ private PsiClass myAndroidContentProvider;
+ private PsiClass myAndroidReceiver;
+ private PsiClass myAndroidView;
+ private PsiClass myAndroidActionProvider;
+ private PsiClass myAndroidParcelable;
private RefPackage myDefaultPackage;
private THashMap<String, RefPackage> myPackages;
private final RefManagerImpl myRefManager;
@@ -66,9 +76,23 @@ public class RefJavaManagerImpl extends RefJavaManager {
LOG.error(e);
}
- myApplet = JavaPsiFacade.getInstance(psiManager.getProject()).findClass("java.applet.Applet", GlobalSearchScope.allScope(project));
- myServlet = JavaPsiFacade.getInstance(psiManager.getProject()).findClass("javax.servlet.Servlet", GlobalSearchScope.allScope(project));
-
+ GlobalSearchScope scope = GlobalSearchScope.allScope(project);
+ JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(psiManager.getProject());
+ myApplet = psiFacade.findClass("java.applet.Applet", scope);
+ myServlet = psiFacade.findClass("javax.servlet.Servlet", scope);
+
+ // Android Framework APIs that apps extend and where the subclasses must be public
+ // such that the framework can instantiate them
+ myAndroidActivity = psiFacade.findClass("android.app.Activity", scope);
+ myAndroidService = psiFacade.findClass("android.app.Service", scope);
+ myAndroidFragment = psiFacade.findClass("android.app.Fragment", scope);
+ myAndroidV4Fragment = psiFacade.findClass("android.support.v4.app.Fragment", scope);
+ myAndroidContentProvider = psiFacade.findClass("android.content.ContentProvider", scope);
+ myAndroidReceiver = psiFacade.findClass("android.content.BroadcastReceiver", scope);
+ myAndroidView = psiFacade.findClass("android.view.View", scope);
+ myAndroidActionProvider = psiFacade.findClass("android.view.ActionProvider", scope);
+ myAndroidParcelable = psiFacade.findClass("android.os.Parcelable", scope);
+ myAndroidBackupAgent = psiFacade.findClass("android.app.backup.BackupAgent", scope);
}
@Override
@@ -158,6 +182,51 @@ public class RefJavaManagerImpl extends RefJavaManager {
}
@Override
+ public PsiClass getAndroidActivity() {
+ return myAndroidActivity;
+ }
+
+ @Override
+ public PsiClass getAndroidService() {
+ return myAndroidService;
+ }
+
+ @Override
+ public PsiClass getAndroidBackupAgent() {
+ return myAndroidBackupAgent;
+ }
+
+ @Override
+ public PsiClass getAndroidFragment(boolean support) {
+ return support ? myAndroidV4Fragment : myAndroidFragment;
+ }
+
+ @Override
+ public PsiClass getAndroidContentProvider() {
+ return myAndroidContentProvider;
+ }
+
+ @Override
+ public PsiClass getAndroidReceiver() {
+ return myAndroidReceiver;
+ }
+
+ @Override
+ public PsiClass getAndroidView() {
+ return myAndroidView;
+ }
+
+ @Override
+ public PsiClass getAndroidActionProvider() {
+ return myAndroidActionProvider;
+ }
+
+ @Override
+ public PsiClass getAndroidParcelable() {
+ return myAndroidParcelable;
+ }
+
+ @Override
public RefParameter getParameterReference(PsiParameter param, int index) {
LOG.assertTrue(myRefManager.isValidPointForReference(), "References may become invalid after process is finished");
RefElement ref = myRefManager.getFromRefTable(param);
@@ -203,6 +272,16 @@ public class RefJavaManagerImpl extends RefJavaManager {
myAppPremainPattern = null;
myAppAgentmainPattern = null;
myServlet = null;
+ myAndroidActivity = null;
+ myAndroidService = null;
+ myAndroidBackupAgent = null;
+ myAndroidFragment = null;
+ myAndroidV4Fragment = null;
+ myAndroidContentProvider = null;
+ myAndroidReceiver = null;
+ myAndroidView = null;
+ myAndroidActionProvider = null;
+ myAndroidParcelable = null;
myDefaultPackage = null;
myProjectIterator = null;
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java
index fd66f2806cb9..aa772f337610 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java
@@ -44,7 +44,6 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.usageView.UsageViewTypeLocation;
import com.intellij.util.IncorrectOperationException;
-import com.intellij.util.VisibilityUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -179,6 +178,10 @@ public class VisibilityInspection extends GlobalJavaBatchInspectionTool {
RefClass refClass = (RefClass) refElement;
if (refClass.isAnonymous() || refClass.isEntry() || refClass.isTestCase() || refClass.isServlet() || refClass.isApplet() || refClass.isLocalClass()) return null;
if (isTopLevelClass(refClass) && !SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES) return null;
+
+ if (refClass.isAndroidPublic()) {
+ return null;
+ }
}
//ignore unreferenced code. They could be a potential entry points.