private TypeSubstitution inferTypeSubstitutionRec(MethodJavaSymbol method, List<JavaType> formals, List<JavaType> argTypes) { boolean isVarArgs = method.isVarArgs(); int numberFormals = formals.size(); int numberArgs = argTypes.size(); int numberParamToCheck = Math.min(numberFormals, numberArgs); List<JavaType> newArgTypes = new ArrayList<>(argTypes); TypeSubstitution substitution = new TypeSubstitution(); // method is varargs but parameter is not provided if (isVarArgs && numberFormals == numberArgs + 1) { numberParamToCheck += 1; newArgTypes.add(symbols.objectType); } for (int i = 0; i < numberParamToCheck; i++) { JavaType formalType = formals.get(i); JavaType argType = newArgTypes.get(i); boolean variableArity = isVarArgs && i == (numberFormals - 1); List<JavaType> remainingArgTypes = new ArrayList<>(newArgTypes.subList(i, newArgTypes.size())); substitution = inferTypeSubstitution(method, substitution, formalType, argType, variableArity, remainingArgTypes); if (substitution.isUnchecked() || (!method.isConstructor() && substitution.typeVariables().containsAll(method.typeVariableTypes))) { // we found all the substitution break; } } return substitution; }
private TypeSubstitution inferTypeSubstitutionRec(MethodJavaSymbol method, List<JavaType> formals, List<JavaType> argTypes) { boolean isVarArgs = method.isVarArgs(); int numberFormals = formals.size(); int numberArgs = argTypes.size(); int numberParamToCheck = Math.min(numberFormals, numberArgs); List<JavaType> newArgTypes = new ArrayList<>(argTypes); TypeSubstitution substitution = new TypeSubstitution(); // method is varargs but parameter is not provided if (isVarArgs && numberFormals == numberArgs + 1) { numberParamToCheck += 1; newArgTypes.add(symbols.objectType); } for (int i = 0; i < numberParamToCheck; i++) { JavaType formalType = formals.get(i); JavaType argType = newArgTypes.get(i); boolean variableArity = isVarArgs && i == (numberFormals - 1); List<JavaType> remainingArgTypes = new ArrayList<>(newArgTypes.subList(i, newArgTypes.size())); substitution = inferTypeSubstitution(method, substitution, formalType, argType, variableArity, remainingArgTypes); if (substitution.isUnchecked() || (!method.isConstructor() && substitution.typeVariables().containsAll(method.typeVariableTypes))) { // we found all the substitution break; } } return substitution; }
/** * @return true, if signature of m1 is more specific than signature of m2 */ private boolean isSignatureMoreSpecific(JavaSymbol m1, JavaSymbol m2, List<JavaType> argTypes, TypeSubstitution m1Substitution, TypeSubstitution m2Substitution) { List<JavaType> m1ArgTypes = ((MethodJavaType) m1.type).argTypes; List<JavaType> m2ArgTypes = ((MethodJavaType) m2.type).argTypes; JavaSymbol.MethodJavaSymbol methodJavaSymbol = (JavaSymbol.MethodJavaSymbol) m1; boolean m1VarArity = methodJavaSymbol.isVarArgs(); boolean m2VarArity = ((JavaSymbol.MethodJavaSymbol) m2).isVarArgs(); if (m1VarArity != m2VarArity) { // last arg is an array boolean lastArgIsArray = !argTypes.isEmpty() && argTypes.get(argTypes.size() -1).isArray() && (argTypes.size() == m2ArgTypes.size() || argTypes.size() == m1ArgTypes.size()); // general case : prefer strict arity invocation over varArity, so if m2 is variadic, m1 is most specific, but not if last arg of invocation is an array return lastArgIsArray ^ m2VarArity; } if (m1VarArity) { m1ArgTypes = expandVarArgsToFitSize(m1ArgTypes, m2ArgTypes.size()); } if(!hasCompatibleArity(m1ArgTypes.size(), m2ArgTypes.size(), m2VarArity)) { return false; } m1ArgTypes = typeSubstitutionSolver.applySubstitutionToFormalParameters(m1ArgTypes, m1Substitution); m2ArgTypes = typeSubstitutionSolver.applySubstitutionToFormalParameters(m2ArgTypes, m2Substitution); return isArgumentsAcceptable(m1ArgTypes, m2ArgTypes, m2VarArity, true); }
public static Flow flowsForArgumentsChangingName(List<Integer> argumentIndices, MethodInvocationTree mit) { JavaSymbol.MethodJavaSymbol methodSymbol = (JavaSymbol.MethodJavaSymbol) mit.symbol(); MethodTree declaration = methodSymbol.declaration(); if (declaration == null) { return Flow.empty(); } Flow.Builder flowBuilder = Flow.builder(); List<VariableTree> methodParameters = declaration.parameters(); for (Integer argumentIndex : argumentIndices) { // do not consider varargs part if (methodSymbol.isVarArgs() && argumentIndex >= methodParameters.size() - 1) { break; } IdentifierTree argumentName = getArgumentIdentifier(mit, argumentIndex); if (argumentName != null) { IdentifierTree parameterIdentifier = methodParameters.get(argumentIndex).simpleName(); String identifierName = parameterIdentifier.name(); if (!argumentName.name().equals(identifierName)) { flowBuilder.add(new JavaFileScannerContext.Location(String.format(IMPLIES_SAME_VALUE, identifierName, argumentName.name()), parameterIdentifier)); } } } return flowBuilder.build().reverse(); }
/** * @param candidate candidate * @param bestSoFar previously found best match */ private JavaSymbol selectBest(Env env, JavaSymbol.TypeJavaSymbol site, List<JavaType> argTypes, JavaSymbol candidate, JavaSymbol bestSoFar, boolean autoboxing) { // TODO get rid of null check if (candidate.kind >= JavaSymbol.ERRONEOUS || !isInheritedIn(candidate, site) || candidate.type == null) { return bestSoFar; } JavaSymbol.MethodJavaSymbol methodJavaSymbol = (JavaSymbol.MethodJavaSymbol) candidate; boolean isVarArgs = methodJavaSymbol.isVarArgs(); boolean usesTypeParameter = usesTypeParameter(methodJavaSymbol); if (!isArgumentsAcceptable(argTypes, ((JavaType.MethodJavaType) candidate.type).argTypes, isVarArgs, autoboxing, usesTypeParameter)) { return bestSoFar; } // TODO ambiguity, errors, ... if (!isAccessible(env, site, candidate)) { return new AccessErrorJavaSymbol(candidate, Symbols.unknownType); } JavaSymbol mostSpecific = selectMostSpecific(candidate, bestSoFar); if (mostSpecific.isKind(JavaSymbol.AMBIGUOUS)) { // same signature, we keep the first symbol found (overrides the other one). mostSpecific = bestSoFar; } return mostSpecific; }
private void checkInvokedMethod(JavaSymbol.MethodJavaSymbol methodSymbol, @Nullable MethodJavaType methodType, ExpressionTree lastArg) { if (methodSymbol.isVarArgs() && lastArg.is(Tree.Kind.NEW_ARRAY)) { if (lastParamHasSameType(methodSymbol, methodType, lastArg.symbolType())) { String message = "Remove this array creation"; NewArrayTree newArrayTree = (NewArrayTree) lastArg; if (newArrayTree.openBraceToken() == null) { ExpressionTree expression = newArrayTree.dimensions().get(0).expression(); Integer literalValue = LiteralUtils.intLiteralValue(expression); if (literalValue == null || literalValue != 0 || isCallingOverload(methodSymbol, lastArg)) { return; } } else if (!newArrayTree.initializers().isEmpty()) { message += " and simply pass the elements"; } reportIssue(lastArg, message + "."); } else { String type = ((Type.ArrayType) getLastParameterType(methodSymbol.parameterTypes())).elementType().name(); reportIssue(lastArg, "Disambiguate this call by either casting as \"" + type + "\" or \"" + type + "[]\"."); } } }
public static Flow flowsForArgumentsChangingName(List<Integer> argumentIndices, MethodInvocationTree mit) { JavaSymbol.MethodJavaSymbol methodSymbol = (JavaSymbol.MethodJavaSymbol) mit.symbol(); MethodTree declaration = methodSymbol.declaration(); if (declaration == null) { return Flow.empty(); } Flow.Builder flowBuilder = Flow.builder(); List<VariableTree> methodParameters = declaration.parameters(); for (Integer argumentIndex : argumentIndices) { // do not consider varargs part if (methodSymbol.isVarArgs() && argumentIndex >= methodParameters.size() - 1) { break; } IdentifierTree argumentName = getArgumentIdentifier(mit, argumentIndex); if (argumentName != null) { IdentifierTree parameterIdentifier = methodParameters.get(argumentIndex).simpleName(); String identifierName = parameterIdentifier.name(); if (!argumentName.name().equals(identifierName)) { flowBuilder.add(new JavaFileScannerContext.Location(String.format(IMPLIES_SAME_VALUE, identifierName, argumentName.name()), parameterIdentifier)); } } } return flowBuilder.build().reverse(); }
private void checkInvokedMethod(JavaSymbol.MethodJavaSymbol methodSymbol, @Nullable MethodJavaType methodType, ExpressionTree lastArg) { if (methodSymbol.isVarArgs() && lastArg.is(Tree.Kind.NEW_ARRAY)) { if (lastParamHasSameType(methodSymbol, methodType, lastArg.symbolType())) { String message = "Remove this array creation"; NewArrayTree newArrayTree = (NewArrayTree) lastArg; if (newArrayTree.openBraceToken() == null) { ExpressionTree expression = newArrayTree.dimensions().get(0).expression(); Integer literalValue = LiteralUtils.intLiteralValue(expression); if (literalValue == null || literalValue != 0 || isCallingOverload(methodSymbol, lastArg)) { return; } } else if (!newArrayTree.initializers().isEmpty()) { message += " and simply pass the elements"; } reportIssue(lastArg, message + "."); } else { String type = ((Type.ArrayType) getLastParameterType(methodSymbol.parameterTypes())).elementType().name(); reportIssue(lastArg, "Disambiguate this call by either casting as \"" + type + "\" or \"" + type + "[]\"."); } } }
private void checkParameters(Tree syntaxNode, Symbol symbol, Arguments arguments, ProgramState state) { if (!symbol.isMethodSymbol() || arguments.isEmpty()) { return; } JavaSymbol.MethodJavaSymbol methodSymbol = (JavaSymbol.MethodJavaSymbol) symbol; if (nonNullAnnotationOnParameters(methodSymbol) == null) { // method is not annotated (locally or globally) return; } int nbArguments = arguments.size(); List<SymbolicValue> argumentSVs = getArgumentSVs(state, syntaxNode, nbArguments); List<JavaSymbol> argumentSymbols = methodSymbol.getParameters().scopeSymbols(); int nbArgumentToCheck = Math.min(nbArguments, argumentSymbols.size() - (methodSymbol.isVarArgs() ? 1 : 0)); for (int i = 0; i < nbArgumentToCheck; i++) { ObjectConstraint constraint = state.getConstraint(argumentSVs.get(i), ObjectConstraint.class); if (constraint != null && constraint.isNull() && !parameterIsNullable(methodSymbol, argumentSymbols.get(i))) { reportIssue(syntaxNode, arguments.get(i), methodSymbol); } } }
private void checkParameters(Tree syntaxNode, Symbol symbol, Arguments arguments, ProgramState state) { if (!symbol.isMethodSymbol() || arguments.isEmpty()) { return; } JavaSymbol.MethodJavaSymbol methodSymbol = (JavaSymbol.MethodJavaSymbol) symbol; if (nonNullAnnotationOnParameters(methodSymbol) == null) { // method is not annotated (locally or globally) return; } int nbArguments = arguments.size(); List<SymbolicValue> argumentSVs = getArgumentSVs(state, syntaxNode, nbArguments); List<JavaSymbol> argumentSymbols = methodSymbol.getParameters().scopeSymbols(); int nbArgumentToCheck = Math.min(nbArguments, argumentSymbols.size() - (methodSymbol.isVarArgs() ? 1 : 0)); for (int i = 0; i < nbArgumentToCheck; i++) { ObjectConstraint constraint = state.getConstraint(argumentSVs.get(i), ObjectConstraint.class); if (constraint != null && constraint.isNull() && !parameterIsNullable(methodSymbol, argumentSymbols.get(i))) { reportIssue(syntaxNode, arguments.get(i), methodSymbol); } } }
private Resolution lookupInScope(Env env, JavaType callSite, JavaType site, String name, List<JavaType> argTypes, List<JavaType> typeParams, boolean looseInvocation, boolean varArity, Scope scope, Resolution bestFound) { Resolution bestSoFar = bestFound; // look in site members for (JavaSymbol symbol : scope.lookup(name)) { if (symbol.kind == JavaSymbol.MTH) { boolean diffArity = varArity || (!((JavaSymbol.MethodJavaSymbol) symbol).isVarArgs()) || (argTypes.size() == ((JavaSymbol.MethodJavaSymbol) symbol).parameterTypes().size() && argTypes.get(argTypes.size() -1 ).isArray()); if(diffArity) { Resolution best = selectBest(env, site, callSite, argTypes, typeParams, symbol, bestSoFar, looseInvocation); if (best.symbol == symbol) { bestSoFar = best; } } } } return bestSoFar; }
private void checkArgumentsTypes(List<ExpressionTree> arguments, MethodJavaSymbol methodSymbol) { List<Type> parametersTypes = methodSymbol.parameterTypes(); // FIXME static imports. // FIXME As arguments are not handled for method resolution using static imports, the provided methodSymbol may not match. if (!parametersTypes.isEmpty()) { for (int index = 0; index < arguments.size(); index++) { ExpressionTree argument = arguments.get(index); if (!argument.is(Kind.NULL_LITERAL)) { Type providedType = argument.symbolType(); Type expectedType = getExpectedType(providedType, parametersTypes, index, methodSymbol.isVarArgs()); if ((expectedType.is("java.lang.Runnable") && providedType.isSubtypeOf("java.lang.Thread")) || (expectedType.is("java.lang.Runnable[]") && (providedType.isSubtypeOf("java.lang.Thread[]")))) { addIssue(argument, getMessage(argument, providedType, index)); } } } } }
private Resolution lookupInScope(Env env, JavaType callSite, JavaType site, String name, List<JavaType> argTypes, List<JavaType> typeParams, boolean looseInvocation, boolean varArity, Scope scope, Resolution bestFound) { Resolution bestSoFar = bestFound; // look in site members for (JavaSymbol symbol : scope.lookup(name)) { if (symbol.kind == JavaSymbol.MTH) { boolean diffArity = varArity || (!((JavaSymbol.MethodJavaSymbol) symbol).isVarArgs()) || (argTypes.size() == ((JavaSymbol.MethodJavaSymbol) symbol).parameterTypes().size() && argTypes.get(argTypes.size() -1 ).isArray()); if(diffArity) { Resolution best = selectBest(env, site, callSite, argTypes, typeParams, symbol, bestSoFar, looseInvocation); if (best.symbol == symbol) { bestSoFar = best; } } } } return bestSoFar; }
private void checkArgumentsTypes(List<ExpressionTree> arguments, MethodJavaSymbol methodSymbol) { List<Type> parametersTypes = methodSymbol.parameterTypes(); // FIXME static imports. // FIXME As arguments are not handled for method resolution using static imports, the provided methodSymbol may not match. if (!parametersTypes.isEmpty()) { for (int index = 0; index < arguments.size(); index++) { ExpressionTree argument = arguments.get(index); Type providedType = argument.symbolType(); if (!argument.is(Kind.NULL_LITERAL) && isThreadAsRunnable(providedType, parametersTypes, index, methodSymbol.isVarArgs())) { reportIssue(argument, getMessage(argument, providedType, index)); } } } }
private void checkArgumentsTypes(List<ExpressionTree> arguments, MethodJavaSymbol methodSymbol) { List<Type> parametersTypes = methodSymbol.parameterTypes(); // FIXME static imports. // FIXME As arguments are not handled for method resolution using static imports, the provided methodSymbol may not match. if (!parametersTypes.isEmpty()) { for (int index = 0; index < arguments.size(); index++) { ExpressionTree argument = arguments.get(index); Type providedType = argument.symbolType(); if (!argument.is(Kind.NULL_LITERAL) && isThreadAsRunnable(providedType, parametersTypes, index, methodSymbol.isVarArgs())) { reportIssue(argument, getMessage(argument, providedType, index)); } } } }
/** * @return true, if signature of m1 is more specific than signature of m2 */ private boolean isSignatureMoreSpecific(JavaSymbol m1, JavaSymbol m2) { List<JavaType> m1ArgTypes = ((JavaType.MethodJavaType) m1.type).argTypes; List<JavaType> m2ArgTypes = ((JavaType.MethodJavaType) m2.type).argTypes; JavaSymbol.MethodJavaSymbol methodJavaSymbol = (JavaSymbol.MethodJavaSymbol) m1; if (methodJavaSymbol.isVarArgs()) { m1ArgTypes = expandVarArgsToFitSize(m1ArgTypes, m2ArgTypes.size()); } boolean usesTypeParameter = usesTypeParameter(methodJavaSymbol); return isArgumentsAcceptable(m1ArgTypes, m2ArgTypes, ((JavaSymbol.MethodJavaSymbol) m2).isVarArgs(), false, usesTypeParameter); }
private boolean hasSameArity(Symbol.MethodSymbol methodUsed, MethodInvocationTree mit) { int formalArity = methodUsed.parameterTypes().size(); int invokedArity = mit.arguments().size(); return formalArity == invokedArity || (((JavaSymbol.MethodJavaSymbol) methodUsed).isVarArgs() && invokedArity >= formalArity - 1); }
public MethodBehavior methodBehaviorForSymbol(Symbol.MethodSymbol symbol) { String signature = symbol.signature(); boolean varArgs = ((JavaSymbol.MethodJavaSymbol) symbol).isVarArgs(); return behaviors.computeIfAbsent(signature, k -> new MethodBehavior(signature, varArgs)); }
public MethodBehavior methodBehaviorForSymbol(Symbol.MethodSymbol symbol) { String signature = symbol.signature(); boolean varArgs = ((JavaSymbol.MethodJavaSymbol) symbol).isVarArgs(); return behaviors.computeIfAbsent(signature, k -> new MethodBehavior(signature, varArgs)); }
private boolean hasSameArity(Symbol.MethodSymbol methodUsed, MethodInvocationTree mit) { int formalArity = methodUsed.parameterTypes().size(); int invokedArity = mit.arguments().size(); return formalArity == invokedArity || (((JavaSymbol.MethodJavaSymbol) methodUsed).isVarArgs() && invokedArity >= formalArity - 1); }