use of com.google.errorprone.VisitorState in project error-prone by google.
the class EqualsIncompatibleType method compatibilityOfTypes.
public static TypeCompatibilityReport compatibilityOfTypes(Type receiverType, Type argumentType, Set<Type> previousReceiverTypes, Set<Type> previousArgumentTypes, VisitorState state) {
if (receiverType == null || argumentType == null) {
return TypeCompatibilityReport.createCompatibleReport();
}
// 1.7: java.lang.Object can be cast to primitives (implicitly through the boxed primitive type)
if (ASTHelpers.isCastable(argumentType, receiverType, state)) {
return leastUpperBoundGenericMismatch(receiverType, argumentType, previousReceiverTypes, previousArgumentTypes, state);
}
// Otherwise, we explore the superclasses of the receiver type as well as the interfaces it
// implements and we collect all overrides of java.lang.Object.equals(). If one of those
// overrides is inherited by the argument, then we don't flag the equality test.
Types types = state.getTypes();
Predicate<MethodSymbol> equalsPredicate = methodSymbol -> !methodSymbol.isStatic() && ((methodSymbol.flags() & Flags.SYNTHETIC) == 0) && types.isSameType(methodSymbol.getReturnType(), state.getSymtab().booleanType) && methodSymbol.getParameters().size() == 1 && types.isSameType(methodSymbol.getParameters().get(0).type, state.getSymtab().objectType);
Set<MethodSymbol> overridesOfEquals = ASTHelpers.findMatchingMethods(state.getName("equals"), equalsPredicate, receiverType, types);
Symbol argumentClass = ASTHelpers.getUpperBound(argumentType, state.getTypes()).tsym;
for (MethodSymbol method : overridesOfEquals) {
ClassSymbol methodClass = method.enclClass();
if (argumentClass.isSubClass(methodClass, types) && !methodClass.equals(state.getSymtab().objectType.tsym) && !methodClass.equals(state.getSymtab().enumSym)) {
// These should be compatible, but check any generic types for their compatbilities.
return leastUpperBoundGenericMismatch(receiverType, argumentType, previousReceiverTypes, previousArgumentTypes, state);
}
}
return TypeCompatibilityReport.incompatible(receiverType, argumentType);
}
use of com.google.errorprone.VisitorState in project error-prone by google.
the class FloatCast method matchTypeCast.
@Override
public Description matchTypeCast(TypeCastTree tree, VisitorState state) {
Tree parent = state.getPath().getParentPath().getLeaf();
if (!(parent instanceof BinaryTree)) {
return NO_MATCH;
}
BinaryTree binop = (BinaryTree) parent;
if (!binop.getLeftOperand().equals(tree)) {
// the precedence is unambiguous for e.g. `i + (int) f`
return NO_MATCH;
}
if (binop.getKind() != Kind.MULTIPLY) {
// there's a bound on the imprecision for +, -, /
return NO_MATCH;
}
Type castType = ASTHelpers.getType(tree.getType());
Type operandType = ASTHelpers.getType(tree.getExpression());
if (castType == null || operandType == null) {
return NO_MATCH;
}
Symtab symtab = state.getSymtab();
if (isSameType(ASTHelpers.getType(parent), symtab.stringType, state)) {
// string concatenation doesn't count
return NO_MATCH;
}
switch(castType.getKind()) {
case LONG:
case INT:
case SHORT:
case CHAR:
case BYTE:
break;
default:
return NO_MATCH;
}
switch(operandType.getKind()) {
case FLOAT:
case DOUBLE:
break;
default:
return NO_MATCH;
}
if (BLACKLIST.matches(tree.getExpression(), state)) {
return NO_MATCH;
}
if (POW.matches(tree.getExpression(), state)) {
MethodInvocationTree pow = (MethodInvocationTree) tree.getExpression();
if (pow.getArguments().stream().map(ASTHelpers::getType).filter(x -> x != null).map(state.getTypes()::unboxedTypeOrType).map(Type::getKind).allMatch(INTEGRAL::contains)) {
return NO_MATCH;
}
}
// Find the outermost enclosing binop, to suggest e.g. `(long) (f * a * b)` instead of
// `(long) (f * a) * b`.
Tree enclosing = binop;
TreePath path = state.getPath().getParentPath().getParentPath();
while (path != null) {
if (!(path.getLeaf() instanceof BinaryTree)) {
break;
}
BinaryTree enclosingBinop = (BinaryTree) path.getLeaf();
if (!enclosingBinop.getLeftOperand().equals(enclosing)) {
break;
}
enclosing = enclosingBinop;
path = path.getParentPath();
}
return buildDescription(tree).addFix(SuggestedFix.builder().prefixWith(tree.getExpression(), "(").postfixWith(enclosing, ")").build()).addFix(SuggestedFix.builder().prefixWith(tree, "(").postfixWith(tree, ")").build()).build();
}
use of com.google.errorprone.VisitorState in project error-prone by google.
the class FunctionalInterfaceClash method matchClass.
@Override
public Description matchClass(ClassTree tree, VisitorState state) {
ClassSymbol origin = getSymbol(tree);
Types types = state.getTypes();
// collect declared and inherited methods whose signature contains a functional interface
Multimap<String, MethodSymbol> methods = HashMultimap.create();
for (Symbol sym : types.membersClosure(getType(tree), /*skipInterface=*/
false).getSymbols()) {
if (!(sym instanceof MethodSymbol)) {
continue;
}
if (isBugCheckerSuppressed((MethodSymbol) sym)) {
continue;
}
MethodSymbol msym = (MethodSymbol) sym;
if (msym.getParameters().stream().noneMatch(p -> maybeFunctionalInterface(p.type, types, state))) {
continue;
}
if (msym.isConstructor() && !msym.owner.equals(origin)) {
continue;
}
methods.put(functionalInterfaceSignature(state, msym), msym);
}
// (don't report clashes between inherited members)
for (Tree member : tree.getMembers()) {
if (!(member instanceof MethodTree)) {
continue;
}
MethodSymbol msym = getSymbol((MethodTree) member);
if (msym.getParameters().stream().noneMatch(p -> maybeFunctionalInterface(p.type, types, state))) {
continue;
}
Collection<MethodSymbol> clash = new ArrayList<>(methods.removeAll(functionalInterfaceSignature(state, msym)));
// Ignore inherited methods that are overridden in the original class. Note that we have to
// handle transitive inheritance explicitly to handle cases where the visibility of an
// overridded method is expanded somewhere in the type hierarchy.
Deque<MethodSymbol> worklist = new ArrayDeque<>();
worklist.push(msym);
clash.remove(msym);
while (!worklist.isEmpty()) {
MethodSymbol msym2 = worklist.removeFirst();
ImmutableList<MethodSymbol> overrides = clash.stream().filter(m -> msym2.overrides(m, origin, types, /*checkResult=*/
false)).collect(toImmutableList());
worklist.addAll(overrides);
clash.removeAll(overrides);
}
if (!clash.isEmpty()) {
// ignore if there are overridden clashing methods in class
if (ASTHelpers.findSuperMethod(msym, types).isPresent() && clash.stream().anyMatch(methodSymbol -> ASTHelpers.findSuperMethod(methodSymbol, types).isPresent())) {
return NO_MATCH;
}
String message = "When passing lambda arguments to this function, callers will need a cast to" + " disambiguate with: " + clash.stream().map(m -> Signatures.prettyMethodSignature(origin, m)).collect(joining("\n "));
state.reportMatch(buildDescription(member).setMessage(message).build());
}
}
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 BehaviorPreservingChecker(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 CatchFail method deleteFix.
// Extract the argument to a call to assertWithMessage, e.g. in:
// assertWithMessage("message").fail();
Optional<Fix> deleteFix(TryTree tree, ImmutableList<CatchTree> catchBlocks, VisitorState state) {
SuggestedFix.Builder fix = SuggestedFix.builder();
if (tree.getFinallyBlock() != null || catchBlocks.size() < tree.getCatches().size()) {
// If the try statement has a finally region, or other catch blocks, delete only the
// unnecessary blocks.
catchBlocks.stream().forEachOrdered(fix::delete);
} else {
// The try statement has no finally region and all catch blocks are unnecessary. Replace it
// with the try statements, deleting all catches.
List<? extends StatementTree> tryStatements = tree.getBlock().getStatements();
String source = state.getSourceCode().toString();
// Replace the full region to work around a GJF partial formatting bug that prevents it from
// re-indenting unchanged lines. This means that fixes may overlap, but that's (hopefully)
// unlikely.
// TODO(b/24140798): emit more precise replacements if GJF is fixed
fix.replace(tree, source.substring(((JCTree) tryStatements.get(0)).getStartPosition(), state.getEndPosition(Iterables.getLast(tryStatements))));
}
MethodTree enclosing = findEnclosing(state.getPath());
if (enclosing == null) {
// There isn't an enclosing method, possibly because we're in a lambda or initializer block.
return Optional.empty();
}
if (isExpectedExceptionTest(ASTHelpers.getSymbol(enclosing), state)) {
// tests, so don't use that fix for methods annotated with @Test(expected=...).
return Optional.empty();
}
// Fix up the enclosing method's throws declaration to include the new thrown exception types.
Collection<Type> thrownTypes = ASTHelpers.getSymbol(enclosing).getThrownTypes();
Types types = state.getTypes();
// Find all types in the deleted catch blocks that are not already in the throws declaration.
ImmutableList<Type> toThrow = catchBlocks.stream().map(c -> ASTHelpers.getType(c.getParameter())).flatMap(t -> t instanceof UnionClassType ? ImmutableList.copyOf(((UnionClassType) t).getAlternativeTypes()).stream() : Stream.of(t)).filter(t -> thrownTypes.stream().noneMatch(x -> types.isAssignable(t, x))).collect(toImmutableList());
if (!toThrow.isEmpty()) {
if (!TEST_CASE.matches(enclosing, state)) {
// not be a safe local refactoring.
return Optional.empty();
}
String throwsString = toThrow.stream().map(t -> SuggestedFixes.qualifyType(state, fix, t)).distinct().collect(joining(", "));
if (enclosing.getThrows().isEmpty()) {
// Add a new throws declaration.
fix.prefixWith(enclosing.getBody(), "throws " + throwsString);
} else {
// Append to an existing throws declaration.
fix.postfixWith(Iterables.getLast(enclosing.getThrows()), ", " + throwsString);
}
}
return Optional.of(fix.build());
}
Aggregations