use of com.google.errorprone.VisitorState in project error-prone by google.
the class CompileTimeConstantExpressionMatcherTest method assertCompilerMatchesOnAssignment.
// Helper methods.
private void assertCompilerMatchesOnAssignment(final Map<String, Boolean> expectedMatches, String... lines) {
final Matcher<ExpressionTree> matcher = new CompileTimeConstantExpressionMatcher();
final Scanner scanner = new Scanner() {
@Override
public Void visitAssignment(AssignmentTree t, VisitorState state) {
ExpressionTree lhs = t.getVariable();
if (expectedMatches.containsKey(lhs.toString())) {
boolean matches = matcher.matches(t.getExpression(), state);
if (expectedMatches.get(lhs.toString())) {
assertTrue("Matcher should match expression" + t.getExpression(), matches);
} else {
assertFalse("Matcher should not match expression" + t.getExpression(), matches);
}
}
return super.visitAssignment(t, state);
}
};
CompilationTestHelper.newInstance(ScannerSupplier.fromScanner(scanner), getClass()).expectResult(Result.OK).addSourceLines("test/CompileTimeConstantExpressionMatcherTestCase.java", lines).doTest();
}
use of com.google.errorprone.VisitorState in project error-prone by google.
the class ChainingConstructorIgnoresParameter method evaluateCallers.
private Description evaluateCallers(MethodSymbol symbol) {
List<VariableTree> paramTypes = paramTypesForMethod.get(symbol);
if (paramTypes == null) {
// We haven't seen the declaration yet. We'll evaluate the call when we do.
return NO_MATCH;
}
for (Caller caller : callersToEvaluate.removeAll(symbol)) {
VisitorState state = caller.state;
MethodInvocationTree invocation = caller.tree;
MethodTree callerConstructor = state.findEnclosing(MethodTree.class);
if (callerConstructor == null) {
// impossible, at least in compilable code?
continue;
}
Map<String, Type> availableParams = indexTypeByName(callerConstructor.getParameters());
/*
* TODO(cpovirk): Better handling of varargs: If the last parameter type is varargs and it is
* called as varargs (rather than by passing an array), then rewrite the parameter types to
* (p0, p1, ..., p[n-2], p[n-1] = element type of varargs parameter if an argument is
* supplied, p[n] = ditto, etc.). For now, we settle for not crashing in the face of a
* mismatch between the number of parameters declared and the number supplied.
*
* (Use MethodSymbol.isVarArgs.)
*/
for (int i = 0; i < paramTypes.size() && i < invocation.getArguments().size(); i++) {
VariableTree formalParam = paramTypes.get(i);
String formalParamName = formalParam.getName().toString();
Type formalParamType = getType(formalParam.getType());
Type availableParamType = availableParams.get(formalParamName);
ExpressionTree actualParam = invocation.getArguments().get(i);
if (/*
* The caller has no param of this type. (Or if it did, we couldn't determine the type.
* Does that ever happen?) If the param doesn't exist, the caller can't be failing to
* pass it.
*/
availableParamType == null || /*
* We couldn't determine the type of the formal parameter. (Does this ever happen?)
*/
formalParamType == null || /*
* The caller is passing the expected parameter (or "ImmutableList.copyOf(parameter),"
* "new File(parameter)," etc.).
*/
referencesIdentifierWithName(formalParamName, actualParam, state)) {
continue;
}
if (state.getTypes().isAssignable(availableParamType, formalParamType)) {
reportMatch(invocation, state, actualParam, formalParamName);
}
/*
* If formal parameter is of an incompatible type, the caller might in theory still intend
* to pass a dervied expression. For example, "Foo(String file)" might intend to call
* "Foo(File file)" by passing "new File(file)." If this comes up in practice, we could
* provide the dummy suggested fix "someExpression(formalParamName)." However, my research
* suggests that this will rarely if ever be what the user wants.
*/
}
}
// All matches are reported through reportMatch calls instead of return values.
return NO_MATCH;
}
use of com.google.errorprone.VisitorState in project error-prone by google.
the class FunctionalInterfaceMethodChanged method matchMethod.
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
ClassTree enclosingClazz = ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class);
if (tree.getModifiers().getFlags().contains(Modifier.DEFAULT) && IS_FUNCTIONAL_INTERFACE.matches(enclosingClazz, state)) {
Types types = Types.instance(state.context);
Set<Symbol> functionalSuperInterfaceSams = enclosingClazz.getImplementsClause().stream().filter(t -> IS_FUNCTIONAL_INTERFACE.matches(t, state)).map(ASTHelpers::getSymbol).map(TypeSymbol.class::cast).map(// TypeSymbol to single abstract method of the type
types::findDescriptorSymbol).collect(toImmutableSet());
// We designate an override of a superinterface SAM "behavior preserving" if it just
// calls the SAM of this interface.
Symbol thisInterfaceSam = types.findDescriptorSymbol(ASTHelpers.getSymbol(enclosingClazz));
// relatively crude: doesn't verify that the same args are passed in the same order
// so it can get false positives for behavior-preservingness (false negatives for the check)
TreeVisitor<Boolean, VisitorState> behaviorPreserving = new SimpleTreeVisitor<Boolean, VisitorState>(false) {
@Override
public Boolean visitMethod(MethodTree node, VisitorState state) {
return node.getBody() != null && node.getBody().accept(this, state);
}
@Override
public Boolean visitBlock(BlockTree node, VisitorState state) {
return node.getStatements().size() == 1 && Iterables.getOnlyElement(node.getStatements()).accept(this, state);
}
@Override
public Boolean visitExpressionStatement(ExpressionStatementTree node, VisitorState state) {
return node.getExpression().accept(this, state);
}
@Override
public Boolean visitReturn(ReturnTree node, VisitorState state) {
return node.getExpression().accept(this, state);
}
@Override
public Boolean visitMethodInvocation(MethodInvocationTree node, VisitorState state) {
return ASTHelpers.getSymbol(node) == thisInterfaceSam;
}
};
if (!Collections.disjoint(ASTHelpers.findSuperMethods(ASTHelpers.getSymbol(tree), types), functionalSuperInterfaceSams) && !tree.accept(behaviorPreserving, state)) {
return describeMatch(tree);
}
}
return Description.NO_MATCH;
}
use of com.google.errorprone.VisitorState in project error-prone by google.
the class MissingCasesInEnumSwitch method matchSwitch.
@Override
public Description matchSwitch(SwitchTree tree, VisitorState state) {
Type switchType = ASTHelpers.getType(tree.getExpression());
if (switchType.asElement().getKind() != ElementKind.ENUM) {
return Description.NO_MATCH;
}
// default case is present
if (tree.getCases().stream().anyMatch(c -> c.getExpression() == null)) {
return Description.NO_MATCH;
}
ImmutableSet<String> handled = tree.getCases().stream().map(CaseTree::getExpression).filter(IdentifierTree.class::isInstance).map(e -> ((IdentifierTree) e).getName().toString()).collect(toImmutableSet());
Set<String> unhandled = Sets.difference(ASTHelpers.enumValues(switchType.asElement()), handled);
if (unhandled.isEmpty()) {
return Description.NO_MATCH;
}
return buildDescription(tree).setMessage(buildMessage(unhandled)).build();
}
use of com.google.errorprone.VisitorState in project error-prone by google.
the class ComparisonContractViolated method matchMethod.
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
if (tree.getBody() == null) {
return Description.NO_MATCH;
}
// Test that the match is in a Comparable.compareTo or Comparator.compare method.
ClassTree declaringClass = ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class);
if (!COMPARABLE_CLASS_MATCHER.matches(declaringClass, state) && !COMPARATOR_CLASS_MATCHER.matches(declaringClass, state)) {
return Description.NO_MATCH;
}
if (!COMPARABLE_METHOD_MATCHER.matches(tree, state) && !COMPARATOR_METHOD_MATCHER.matches(tree, state)) {
return Description.NO_MATCH;
}
final Set<ComparisonResult> seenResults = EnumSet.noneOf(ComparisonResult.class);
final TreeVisitor<Void, VisitorState> visitReturnExpression = new SimpleTreeVisitor<Void, VisitorState>() {
@Override
protected Void defaultAction(Tree node, VisitorState state) {
seenResults.add(node.accept(CONSTANT_VISITOR, state));
return null;
}
@Override
public Void visitConditionalExpression(ConditionalExpressionTree node, VisitorState state) {
node.getTrueExpression().accept(this, state);
node.getFalseExpression().accept(this, state);
return null;
}
};
tree.getBody().accept(new TreeScanner<Void, VisitorState>() {
@Override
public Void visitReturn(ReturnTree node, VisitorState state) {
return node.getExpression().accept(visitReturnExpression, state);
}
}, state);
if (seenResults.isEmpty() || seenResults.contains(ComparisonResult.NONCONSTANT)) {
return Description.NO_MATCH;
}
if (!seenResults.contains(ComparisonResult.ZERO)) {
if (tree.getBody().getStatements().size() == 1 && tree.getBody().getStatements().get(0).getKind() == Kind.RETURN) {
ReturnTree returnTree = (ReturnTree) tree.getBody().getStatements().get(0);
if (returnTree.getExpression().getKind() == Kind.CONDITIONAL_EXPRESSION) {
ConditionalExpressionTree condTree = (ConditionalExpressionTree) returnTree.getExpression();
ExpressionTree conditionExpr = condTree.getCondition();
while (conditionExpr instanceof ParenthesizedTree) {
conditionExpr = ((ParenthesizedTree) conditionExpr).getExpression();
}
if (!(conditionExpr instanceof BinaryTree)) {
return describeMatch(tree);
}
ComparisonResult trueConst = condTree.getTrueExpression().accept(CONSTANT_VISITOR, state);
ComparisonResult falseConst = condTree.getFalseExpression().accept(CONSTANT_VISITOR, state);
boolean trueFirst;
if (trueConst == ComparisonResult.NEGATIVE_CONSTANT && falseConst == ComparisonResult.POSITIVE_CONSTANT) {
trueFirst = true;
} else if (trueConst == ComparisonResult.POSITIVE_CONSTANT && falseConst == ComparisonResult.NEGATIVE_CONSTANT) {
trueFirst = false;
} else {
return describeMatch(tree);
}
switch(conditionExpr.getKind()) {
case LESS_THAN:
case LESS_THAN_EQUAL:
break;
case GREATER_THAN:
case GREATER_THAN_EQUAL:
trueFirst = !trueFirst;
break;
default:
return describeMatch(tree);
}
BinaryTree binaryExpr = (BinaryTree) conditionExpr;
Type ty = ASTHelpers.getType(binaryExpr.getLeftOperand());
Types types = Types.instance(state.context);
Symtab symtab = Symtab.instance(state.context);
ExpressionTree first = trueFirst ? binaryExpr.getLeftOperand() : binaryExpr.getRightOperand();
ExpressionTree second = trueFirst ? binaryExpr.getRightOperand() : binaryExpr.getLeftOperand();
String compareType;
if (types.isSameType(ty, symtab.intType)) {
compareType = "Integer";
} else if (types.isSameType(ty, symtab.longType)) {
compareType = "Long";
} else {
return describeMatch(tree);
}
return describeMatch(condTree, SuggestedFix.replace(condTree, String.format("%s.compare(%s, %s)", compareType, state.getSourceForNode(first), state.getSourceForNode(second))));
}
}
return describeMatch(tree);
}
if (COMPARATOR_METHOD_MATCHER.matches(tree, state) && (seenResults.contains(ComparisonResult.NEGATIVE_CONSTANT) != seenResults.contains(ComparisonResult.POSITIVE_CONSTANT))) {
// See e.g. com.google.common.collect.Cut.BelowAll.
return describeMatch(tree);
} else {
return Description.NO_MATCH;
}
}
Aggregations