private static void report( @NonNull JavaContext context, @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) { // Make sure the call is on a view if (!context.getEvaluator().isMemberInSubClassOf(method, CLASS_VIEW, false)) { return; } String name = method.getName(); String suggestion = Character.toLowerCase(name.charAt(2)) + name.substring(3); String message = String.format( // Keep in sync with {@link #getOldValue} and {@link #getNewValue} below! "Suspicious method call; should probably call \"`%1$s`\" rather than \"`%2$s`\"", suggestion, name); context.report(ISSUE, node, context.getNameLocation(node), message); }
@Override public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) { String name = declaration.getName(); if (name == null || !name.equals("DividerItemDecoration")) { return; } if (declaration.findFieldByName("HORIZONTAL_LIST", false) == null || declaration.findFieldByName("VERTICAL_LIST", false) == null) { return; } // Don't warn if this is the actual support library being compiled String qualifiedName = declaration.getQualifiedName(); if ("android.support.v7.widget.DividerItemDecoration".equals(qualifiedName)) { return; } Location location = context.getNameLocation(declaration); context.report(ISSUE, declaration, location, "Replace with `android.support.v7.widget.DividerItemDecoration`?"); } }
@Override public void visitReference( @NonNull JavaContext context, @Nullable JavaElementVisitor visitor, @NonNull PsiJavaCodeReferenceElement reference, @NonNull PsiElement resolved) { JavaEvaluator evaluator = context.getEvaluator(); if (resolved instanceof PsiField && evaluator.isMemberInSubClassOf((PsiField)resolved, "android.os.Build", false)) { String message = String.format(MESSAGE_DEVICE_IDENTIFIERS, "SERIAL"); context.report(ISSUE, reference, context.getNameLocation(reference), message); } }
private static void checkCallSuper(@NonNull JavaContext context, @NonNull PsiMethod method) { PsiMethod superMethod = getRequiredSuperMethod(context, method); if (superMethod != null) { if (!SuperCallVisitor.callsSuper(method, superMethod)) { String methodName = method.getName(); String message = "Overriding method should call `super." + methodName + "`"; Location location = context.getNameLocation(method); context.report(ISSUE, method, location, message); } } }
private void checkClass(@NonNull PsiElement element, @Nullable String descriptor, @NonNull String owner, @NonNull String fqcn) { int api = mApiDatabase.getClassVersion(owner); if (api == -1) { return; } int minSdk = getMinSdk(mContext); if (isSuppressed(mContext, api, element, minSdk)) { return; } // It's okay to reference classes from annotations if (PsiTreeUtil.getParentOfType(element, PsiAnnotation.class) != null) { return; } Location location = mContext.getNameLocation(element); minSdk = Math.max(minSdk, getTargetApi(element)); String message = String.format( "%1$s requires API level %2$d (current min is %3$d): %4$s", descriptor == null ? "Class" : descriptor, api, Math.max(minSdk, getTargetApi(element)), fqcn); mContext.report(UNSUPPORTED, element, location, message); }
@Override public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor, @NonNull PsiMethodCallExpression call, @NonNull PsiMethod method) { if (context.getEvaluator().isMemberInClass(method, TYPE_STRING)) { String name = method.getName(); if (name.equals(FORMAT_METHOD)) { checkFormat(context, method, call); } else if (method.getParameterList().getParametersCount() == 0) { Location location = context.getNameLocation(call); String message = String.format( "Implicitly using the default locale is a common source of bugs: " + "Use `%1$s(Locale)` instead. For strings meant to be internal " + "use `Locale.ROOT`, otherwise `Locale.getDefault()`.", name); context.report(STRING_LOCALE, call, location, message); } } }
private static void reportWrongTag( @NonNull JavaContext context, @NonNull PsiClass node, @NonNull String rightTag, @NonNull String className, @NonNull String framework) { String wrongTag = classToTag(framework); if (wrongTag == null) { return; } Location location = context.getNameLocation(node); String message = String.format("`%1$s` is %2$s but is registered " + "in the manifest as %3$s", className, describeTag(rightTag), describeTag(wrongTag)); context.report(ISSUE, node, location, message); }
location = mContext.getNameLocation(expression); String fqcn = method.getName(); String message = String.format(
@Override public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) { JavaEvaluator evaluator = context.getEvaluator(); for (PsiMethod method : declaration.findMethodsByName("verify", false)) { if (evaluator.methodMatches(method, null, false, TYPE_STRING, "javax.net.ssl.SSLSession")) { ComplexVisitor visitor = new ComplexVisitor(context); declaration.accept(visitor); if (visitor.isComplex()) { return; } Location location = context.getNameLocation(method); String message = String.format("`%1$s` always returns `true`, which " + "could cause insecure network traffic due to trusting " + "TLS/SSL server certificates for wrong hostnames", method.getName()); context.report(ISSUE, method, location, message); break; } } }
Location location = context.getNameLocation(node); String message = String.format("The `<%1$s> %2$s` is not registered in the manifest", tag, className);
@Override public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) { Project project = context.getMainProject(); if (project.dependsOn(APPCOMPAT_LIB_ARTIFACT) != Boolean.TRUE) { return; } PsiClass superClass = declaration.getSuperClass(); if (!hasAppCompatDelegate(context, superClass)) { return; } PsiElement locationNode = declaration; PsiReferenceList extendsList = declaration.getExtendsList(); if (extendsList != null) { PsiJavaCodeReferenceElement[] elements = extendsList.getReferenceElements(); if (elements.length > 0) { locationNode = elements[0]; } } Location location = context.getNameLocation(locationNode); String suggested = getAppCompatDelegate(superClass); // Keep error message in sync with #getSuggestedSuperClass below String message = String.format("This custom view should extend `%1$s` instead", suggested); context.report(ISSUE, declaration, location, message); }
@Override public void visitMethod(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor, @NonNull PsiMethodCallExpression node, @NonNull PsiMethod method) { // Ignore the issue if we never build for any API less than 17. if (context.getMainProject().getMinSdk() >= 17) { return; } JavaEvaluator evaluator = context.getEvaluator(); if (!evaluator.methodMatches(method, WEB_VIEW, true, TYPE_OBJECT, TYPE_STRING)) { return; } String message = "`WebView.addJavascriptInterface` should not be called with minSdkVersion < 17 for security reasons: " + "JavaScript can use reflection to manipulate application"; context.report(ISSUE, node, context.getNameLocation(node), message); } }
@Override public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) { // Only check classes that are not declared abstract. if (!context.getEvaluator().isAbstract(declaration)) { MediaSessionCallbackVisitor visitor = new MediaSessionCallbackVisitor(context); declaration.accept(visitor); if (!visitor.isPlayFromSearchMethodFound() && context.isEnabled(MISSING_ON_PLAY_FROM_SEARCH)) { context.report(MISSING_ON_PLAY_FROM_SEARCH, declaration, context.getNameLocation(declaration), "This class does not override `" + METHOD_MEDIA_SESSION_PLAY_FROM_SEARCH + "` from `MediaSession.Callback`" + " The method should be overridden and implemented to support " + "Voice search on Android Auto."); } } }
@Override public void visitReference(@NonNull JavaContext context, @Nullable JavaElementVisitor visitor, @NonNull PsiJavaCodeReferenceElement reference, @NonNull PsiElement resolved) { if (resolved instanceof PsiField && context.getEvaluator().isMemberInSubClassOf((PsiField)resolved, "android.provider.Settings", false) && context.getMainProject().getTargetSdkVersion().getFeatureLevel() >= 23) { String message = getBatteryOptimizationsErrorMessage(); context.report(ISSUE, reference, context.getNameLocation(reference), message); } }
@Override public void checkClass(@NonNull JavaContext context, @NonNull PsiClass declaration) { if (declaration instanceof PsiAnonymousClass) { // Anonymous classes aren't parcelable return; } // Only applies to concrete classes if (declaration.isInterface()) { return; } if (declaration.hasModifierProperty(PsiModifier.ABSTRACT)) { return; } // Parceling spans is handled in TextUtils#CHAR_SEQUENCE_CREATOR if (context.getEvaluator().implementsInterface(declaration, "android.text.ParcelableSpan", false)) { return; } PsiField field = declaration.findFieldByName("CREATOR", true); if (field == null) { Location location = context.getNameLocation(declaration); context.report(ISSUE, declaration, location, "This class implements `Parcelable` but does not " + "provide a `CREATOR` field"); } } }
+ "or `(Context,AttributeSet,int)`", declaration.getName()); Location location = context.getNameLocation(declaration);
Location location = context.getNameLocation(call); String message = String.format( "None of the methods in the added interface (%1$s) have been annotated " +
Location location = context.getNameLocation(method); String message = getErrorMessage(methodName); context.report(ISSUE, method, location, message);
context.report(ISSUE, element, context.getNameLocation(element), "Possible leak");
"finding can safely be ignored."; Location location = context.getNameLocation(method); context.report(ACTION_STRING, method, location, report);