Search in sources :

Example 6 with UExpression

use of org.jetbrains.uast.UExpression in project kotlin by JetBrains.

the class LocaleDetector method checkFormat.

private static void checkFormat(@NonNull JavaContext context, @NonNull PsiMethod method, @NonNull UCallExpression call) {
    // Only check the non-locale version of String.format
    if (method.getParameterList().getParametersCount() == 0 || !context.getEvaluator().parameterHasType(method, 0, TYPE_STRING)) {
        return;
    }
    List<UExpression> expressions = call.getValueArguments();
    if (expressions.isEmpty()) {
        return;
    }
    // Find the formatting string
    UExpression first = expressions.get(0);
    Object value = ConstantEvaluator.evaluate(context, first);
    if (!(value instanceof String)) {
        return;
    }
    String format = (String) value;
    if (StringFormatDetector.isLocaleSpecific(format)) {
        if (isLoggingParameter(call)) {
            return;
        }
        Location location = context.getUastLocation(call);
        String message = "Implicitly using the default locale is a common source of bugs: " + "Use `String.format(Locale, ...)` instead";
        context.report(STRING_LOCALE, call, location, message);
    }
}
Also used : UExpression(org.jetbrains.uast.UExpression) Location(com.android.tools.klint.detector.api.Location)

Example 7 with UExpression

use of org.jetbrains.uast.UExpression in project kotlin by JetBrains.

the class ViewTagDetector method visitMethod.

@Override
public void visitMethod(@NonNull JavaContext context, @Nullable UastVisitor visitor, @NonNull UCallExpression call, @NonNull UMethod method) {
    // http://code.google.com/p/android/issues/detail?id=18273
    if (context.getMainProject().getMinSdk() >= 14) {
        return;
    }
    JavaEvaluator evaluator = context.getEvaluator();
    if (!evaluator.isMemberInSubClassOf(method, CLASS_VIEW, false)) {
        return;
    }
    List<UExpression> arguments = call.getValueArguments();
    if (arguments.size() != 2) {
        return;
    }
    UExpression tagArgument = arguments.get(1);
    if (tagArgument == null) {
        return;
    }
    PsiType type = TypeEvaluator.evaluate(context, tagArgument);
    if ((!(type instanceof PsiClassType))) {
        return;
    }
    PsiClass typeClass = ((PsiClassType) type).resolve();
    if (typeClass == null) {
        return;
    }
    String objectType;
    if (InheritanceUtil.isInheritor(typeClass, false, CLASS_VIEW)) {
        objectType = "views";
    } else if (InheritanceUtil.isInheritor(typeClass, false, CURSOR_CLS)) {
        objectType = "cursors";
    } else if (typeClass.getName() != null && typeClass.getName().endsWith("ViewHolder")) {
        objectType = "view holders";
    } else {
        return;
    }
    String message = String.format("Avoid setting %1$s as values for `setTag`: " + "Can lead to memory leaks in versions older than Android 4.0", objectType);
    context.report(ISSUE, call, context.getUastLocation(tagArgument), message);
}
Also used : UExpression(org.jetbrains.uast.UExpression) PsiClassType(com.intellij.psi.PsiClassType) PsiClass(com.intellij.psi.PsiClass) JavaEvaluator(com.android.tools.klint.client.api.JavaEvaluator) PsiType(com.intellij.psi.PsiType)

Example 8 with UExpression

use of org.jetbrains.uast.UExpression in project kotlin by JetBrains.

the class StringFormatDetector method checkStringFormatCall.

/**
     * Check the given String.format call (with the given arguments) to see if the string format is
     * being used correctly
     *  @param context           the context to report errors to
     * @param calledMethod      the method being called
     * @param call              the AST node for the {@link String#format}
     * @param specifiesLocale   whether the first parameter is a locale string, shifting the
     */
private void checkStringFormatCall(JavaContext context, PsiMethod calledMethod, UCallExpression call, boolean specifiesLocale) {
    int argIndex = specifiesLocale ? 1 : 0;
    List<UExpression> args = call.getValueArguments();
    if (args.size() <= argIndex) {
        return;
    }
    UExpression argument = args.get(argIndex);
    ResourceUrl resource = ResourceEvaluator.getResource(context, argument);
    if (resource == null || resource.framework || resource.type != ResourceType.STRING) {
        return;
    }
    String name = resource.name;
    if (mIgnoreStrings != null && mIgnoreStrings.contains(name)) {
        return;
    }
    boolean passingVarArgsArray = false;
    int callCount = args.size() - 1 - argIndex;
    if (callCount == 1) {
        // If instead of a varargs call like
        //    getString(R.string.foo, arg1, arg2, arg3)
        // the code is calling the varargs method with a packed Object array, as in
        //    getString(R.string.foo, new Object[] { arg1, arg2, arg3 })
        // we'll need to handle that such that we don't think this is a single
        // argument
        UExpression lastArg = args.get(args.size() - 1);
        PsiParameterList parameterList = calledMethod.getParameterList();
        int parameterCount = parameterList.getParametersCount();
        if (parameterCount > 0 && parameterList.getParameters()[parameterCount - 1].isVarArgs()) {
            boolean knownArity = false;
            boolean argWasReference = false;
            if (lastArg instanceof UReferenceExpression) {
                PsiElement resolved = ((UReferenceExpression) lastArg).resolve();
                if (resolved instanceof PsiVariable) {
                    UExpression initializer = context.getUastContext().getInitializerBody((PsiVariable) resolved);
                    if (initializer != null && (UastExpressionUtils.isNewArray(initializer) || UastExpressionUtils.isArrayInitializer(initializer))) {
                        argWasReference = true;
                        // Now handled by check below
                        lastArg = initializer;
                    }
                }
            }
            if (UastExpressionUtils.isNewArray(lastArg) || UastExpressionUtils.isArrayInitializer(lastArg)) {
                UCallExpression arrayInitializer = (UCallExpression) lastArg;
                if (UastExpressionUtils.isNewArrayWithInitializer(lastArg) || UastExpressionUtils.isArrayInitializer(lastArg)) {
                    callCount = arrayInitializer.getValueArgumentCount();
                    knownArity = true;
                } else if (UastExpressionUtils.isNewArrayWithDimensions(lastArg)) {
                    List<UExpression> arrayDimensions = arrayInitializer.getValueArguments();
                    if (arrayDimensions.size() == 1) {
                        UExpression first = arrayDimensions.get(0);
                        if (first instanceof ULiteralExpression) {
                            Object o = ((ULiteralExpression) first).getValue();
                            if (o instanceof Integer) {
                                callCount = (Integer) o;
                                knownArity = true;
                            }
                        }
                    }
                }
                if (!knownArity) {
                    if (!argWasReference) {
                        return;
                    }
                } else {
                    passingVarArgsArray = true;
                }
            }
        }
    }
    if (callCount > 0 && mNotFormatStrings.containsKey(name)) {
        checkNotFormattedHandle(context, call, name, mNotFormatStrings.get(name));
        return;
    }
    List<Pair<Handle, String>> list = mFormatStrings != null ? mFormatStrings.get(name) : null;
    if (list == null) {
        LintClient client = context.getClient();
        if (client.supportsProjectResources() && !context.getScope().contains(Scope.RESOURCE_FILE)) {
            AbstractResourceRepository resources = client.getProjectResources(context.getMainProject(), true);
            List<ResourceItem> items;
            if (resources != null) {
                items = resources.getResourceItem(ResourceType.STRING, name);
            } else {
                // Must be a non-Android module
                items = null;
            }
            if (items != null) {
                for (final ResourceItem item : items) {
                    ResourceValue v = item.getResourceValue(false);
                    if (v != null) {
                        String value = v.getRawXmlValue();
                        if (value != null) {
                            // Make sure it's really a formatting string,
                            // not for example "Battery remaining: 90%"
                            boolean isFormattingString = value.indexOf('%') != -1;
                            for (int j = 0, m = value.length(); j < m && isFormattingString; j++) {
                                char c = value.charAt(j);
                                if (c == '\\') {
                                    j++;
                                } else if (c == '%') {
                                    Matcher matcher = FORMAT.matcher(value);
                                    if (!matcher.find(j)) {
                                        isFormattingString = false;
                                    } else {
                                        String conversion = matcher.group(6);
                                        int conversionClass = getConversionClass(conversion.charAt(0));
                                        if (conversionClass == CONVERSION_CLASS_UNKNOWN || matcher.group(5) != null) {
                                            // Some date format etc - don't process
                                            return;
                                        }
                                    }
                                    // Don't process second % in a %%
                                    j++;
                                }
                            // If the user marked the string with
                            }
                            Handle handle = client.createResourceItemHandle(item);
                            if (isFormattingString) {
                                if (list == null) {
                                    list = Lists.newArrayList();
                                    if (mFormatStrings == null) {
                                        mFormatStrings = Maps.newHashMap();
                                    }
                                    mFormatStrings.put(name, list);
                                }
                                list.add(Pair.of(handle, value));
                            } else if (callCount > 0) {
                                checkNotFormattedHandle(context, call, name, handle);
                            }
                        }
                    }
                }
            }
        } else {
            return;
        }
    }
    if (list != null) {
        Set<String> reported = null;
        for (Pair<Handle, String> pair : list) {
            String s = pair.getSecond();
            if (reported != null && reported.contains(s)) {
                continue;
            }
            int count = getFormatArgumentCount(s, null);
            Handle handle = pair.getFirst();
            if (count != callCount) {
                Location location = context.getUastLocation(call);
                Location secondary = handle.resolve();
                secondary.setMessage(String.format("This definition requires %1$d arguments", count));
                location.setSecondary(secondary);
                String message = String.format("Wrong argument count, format string `%1$s` requires `%2$d` but format " + "call supplies `%3$d`", name, count, callCount);
                context.report(ARG_TYPES, call, location, message);
                if (reported == null) {
                    reported = Sets.newHashSet();
                }
                reported.add(s);
            } else {
                if (passingVarArgsArray) {
                    // flag parameters on the Object[] instead of the wrapped parameters
                    return;
                }
                for (int i = 1; i <= count; i++) {
                    int argumentIndex = i + argIndex;
                    PsiType type = args.get(argumentIndex).getExpressionType();
                    if (type != null) {
                        boolean valid = true;
                        String formatType = getFormatArgumentType(s, i);
                        if (formatType == null) {
                            continue;
                        }
                        char last = formatType.charAt(formatType.length() - 1);
                        if (formatType.length() >= 2 && Character.toLowerCase(formatType.charAt(formatType.length() - 2)) == 't') {
                            // TODO
                            continue;
                        }
                        switch(last) {
                            // unusual and probably not intended.
                            case 'b':
                            case 'B':
                                valid = isBooleanType(type);
                                break;
                            // Numeric: integer and floats in various formats
                            case 'x':
                            case 'X':
                            case 'd':
                            case 'o':
                            case 'e':
                            case 'E':
                            case 'f':
                            case 'g':
                            case 'G':
                            case 'a':
                            case 'A':
                                valid = isNumericType(type, true);
                                break;
                            case 'c':
                            case 'C':
                                // Unicode character
                                valid = isCharacterType(type);
                                break;
                            case 'h':
                            // Hex print of hash code of objects
                            case 'H':
                            case 's':
                            case 'S':
                                // String. Can pass anything, but warn about
                                // numbers since you may have meant more
                                // specific formatting. Use special issue
                                // explanation for this?
                                valid = !isBooleanType(type) && !isNumericType(type, false);
                                break;
                        }
                        if (!valid) {
                            Location location = context.getUastLocation(args.get(argumentIndex));
                            Location secondary = handle.resolve();
                            secondary.setMessage("Conflicting argument declaration here");
                            location.setSecondary(secondary);
                            String suggestion = null;
                            if (isBooleanType(type)) {
                                suggestion = "`b`";
                            } else if (isCharacterType(type)) {
                                suggestion = "'c'";
                            } else if (PsiType.INT.equals(type) || PsiType.LONG.equals(type) || PsiType.BYTE.equals(type) || PsiType.SHORT.equals(type)) {
                                suggestion = "`d`, 'o' or `x`";
                            } else if (PsiType.FLOAT.equals(type) || PsiType.DOUBLE.equals(type)) {
                                suggestion = "`e`, 'f', 'g' or `a`";
                            } else if (type instanceof PsiClassType) {
                                String fqn = type.getCanonicalText();
                                if (TYPE_INTEGER_WRAPPER.equals(fqn) || TYPE_LONG_WRAPPER.equals(fqn) || TYPE_BYTE_WRAPPER.equals(fqn) || TYPE_SHORT_WRAPPER.equals(fqn)) {
                                    suggestion = "`d`, 'o' or `x`";
                                } else if (TYPE_FLOAT_WRAPPER.equals(fqn) || TYPE_DOUBLE_WRAPPER.equals(fqn)) {
                                    suggestion = "`d`, 'o' or `x`";
                                } else if (TYPE_OBJECT.equals(fqn)) {
                                    suggestion = "'s' or 'h'";
                                }
                            }
                            if (suggestion != null) {
                                suggestion = " (Did you mean formatting character " + suggestion + "?)";
                            } else {
                                suggestion = "";
                            }
                            String canonicalText = type.getCanonicalText();
                            canonicalText = canonicalText.substring(canonicalText.lastIndexOf('.') + 1);
                            String message = String.format("Wrong argument type for formatting argument '#%1$d' " + "in `%2$s`: conversion is '`%3$s`', received `%4$s` " + "(argument #%5$d in method call)%6$s", i, name, formatType, canonicalText, argumentIndex + 1, suggestion);
                            context.report(ARG_TYPES, call, location, message);
                            if (reported == null) {
                                reported = Sets.newHashSet();
                            }
                            reported.add(s);
                        }
                    }
                }
            }
        }
    }
}
Also used : ULiteralExpression(org.jetbrains.uast.ULiteralExpression) Matcher(java.util.regex.Matcher) UCallExpression(org.jetbrains.uast.UCallExpression) AbstractResourceRepository(com.android.ide.common.res2.AbstractResourceRepository) ResourceValue(com.android.ide.common.rendering.api.ResourceValue) List(java.util.List) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) NodeList(org.w3c.dom.NodeList) PsiParameterList(com.intellij.psi.PsiParameterList) ResourceUrl(com.android.ide.common.resources.ResourceUrl) PsiElement(com.intellij.psi.PsiElement) Pair(com.android.utils.Pair) PsiType(com.intellij.psi.PsiType) PsiVariable(com.intellij.psi.PsiVariable) LintClient(com.android.tools.klint.client.api.LintClient) Handle(com.android.tools.klint.detector.api.Location.Handle) UExpression(org.jetbrains.uast.UExpression) PsiClassType(com.intellij.psi.PsiClassType) UReferenceExpression(org.jetbrains.uast.UReferenceExpression) PsiParameterList(com.intellij.psi.PsiParameterList) ResourceItem(com.android.ide.common.res2.ResourceItem) Location(com.android.tools.klint.detector.api.Location)

Example 9 with UExpression

use of org.jetbrains.uast.UExpression in project kotlin by JetBrains.

the class GetSignaturesDetector method visitMethod.

@Override
public void visitMethod(@NonNull JavaContext context, @Nullable UastVisitor visitor, @NonNull UCallExpression node, @NonNull UMethod method) {
    JavaEvaluator evaluator = context.getEvaluator();
    if (!evaluator.methodMatches(method, PACKAGE_MANAGER_CLASS, true, JavaParser.TYPE_STRING, JavaParser.TYPE_INT)) {
        return;
    }
    List<UExpression> arguments = node.getValueArguments();
    UExpression second = arguments.get(1);
    Object number = ConstantEvaluator.evaluate(context, second);
    if (number instanceof Number) {
        int flagValue = ((Number) number).intValue();
        maybeReportIssue(flagValue, context, node, second);
    }
}
Also used : UExpression(org.jetbrains.uast.UExpression) JavaEvaluator(com.android.tools.klint.client.api.JavaEvaluator)

Example 10 with UExpression

use of org.jetbrains.uast.UExpression in project kotlin by JetBrains.

the class TrustAllX509TrustManagerDetector method checkMethod.

private static void checkMethod(@NonNull JavaContext context, @NonNull UClass cls, @NonNull String methodName) {
    JavaEvaluator evaluator = context.getEvaluator();
    for (PsiMethod method : cls.findMethodsByName(methodName, true)) {
        if (evaluator.isAbstract(method)) {
            continue;
        }
        // For now very simple; only checks if nothing is done.
        // Future work: Improve this check to be less sensitive to irrelevant
        // instructions/statements/invocations (e.g. System.out.println) by
        // looking for calls that could lead to a CertificateException being
        // thrown, e.g. throw statement within the method itself or invocation
        // of another method that may throw a CertificateException, and only
        // reporting an issue if none of these calls are found. ControlFlowGraph
        // may be useful here.
        UExpression body = context.getUastContext().getMethodBody(method);
        ComplexBodyVisitor visitor = new ComplexBodyVisitor();
        body.accept(visitor);
        if (!visitor.isComplex()) {
            Location location = context.getNameLocation(method);
            String message = getErrorMessage(methodName);
            context.report(ISSUE, method, location, message);
        }
    }
}
Also used : UExpression(org.jetbrains.uast.UExpression) PsiMethod(com.intellij.psi.PsiMethod) JavaEvaluator(com.android.tools.klint.client.api.JavaEvaluator) Location(com.android.tools.klint.detector.api.Location)

Aggregations

UExpression (org.jetbrains.uast.UExpression)25 Location (com.android.tools.klint.detector.api.Location)8 JavaEvaluator (com.android.tools.klint.client.api.JavaEvaluator)7 PsiElement (com.intellij.psi.PsiElement)6 PsiMethod (com.intellij.psi.PsiMethod)6 PsiClass (com.intellij.psi.PsiClass)5 UCallExpression (org.jetbrains.uast.UCallExpression)5 ResourceUrl (com.android.ide.common.resources.ResourceUrl)4 PsiClassType (com.intellij.psi.PsiClassType)4 PsiType (com.intellij.psi.PsiType)4 UReferenceExpression (org.jetbrains.uast.UReferenceExpression)4 Nullable (com.android.annotations.Nullable)3 LintFix (com.android.tools.lint.detector.api.LintFix)3 PsiVariable (com.intellij.psi.PsiVariable)3 UElement (org.jetbrains.uast.UElement)3 ULiteralExpression (org.jetbrains.uast.ULiteralExpression)3 PsiField (com.intellij.psi.PsiField)2 UIfExpression (org.jetbrains.uast.UIfExpression)2 UParenthesizedExpression (org.jetbrains.uast.UParenthesizedExpression)2 UQualifiedReferenceExpression (org.jetbrains.uast.UQualifiedReferenceExpression)2