use of com.sun.tools.javac.code.Types in project error-prone by google.
the class CollectionIncompatibleType method matchMethodInvocation.
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
MatchResult directResult = firstNonNullMatchResult(DIRECT_MATCHERS, tree, state);
MatchResult typeArgResult = null;
if (directResult == null) {
typeArgResult = firstNonNullMatchResult(TYPE_ARG_MATCHERS, tree, state);
}
if (directResult == null && typeArgResult == null) {
return Description.NO_MATCH;
}
Verify.verify(directResult == null ^ typeArgResult == null);
MatchResult result = MoreObjects.firstNonNull(directResult, typeArgResult);
Types types = state.getTypes();
TypeCompatibilityReport compatibilityReport = EqualsIncompatibleType.compatibilityOfTypes(result.targetType(), result.sourceType(), state);
if (compatibilityReport.compatible()) {
return Description.NO_MATCH;
}
// For error message, use simple names instead of fully qualified names unless they are
// identical.
String sourceTreeType = Signatures.prettyType(ASTHelpers.getType(result.sourceTree()));
String sourceType = Signatures.prettyType(result.sourceType());
String targetType = Signatures.prettyType(result.targetType());
if (sourceType.equals(targetType)) {
sourceType = result.sourceType().toString();
targetType = result.targetType().toString();
}
Description.Builder description = buildDescription(tree);
if (typeArgResult != null) {
description.setMessage(String.format("Argument '%s' should not be passed to this method; its type %s has a type argument " + "%s that is not compatible with its collection's type argument %s", result.sourceTree(), sourceTreeType, sourceType, targetType));
} else {
description.setMessage(String.format("Argument '%s' should not be passed to this method; its type %s is not compatible " + "with its collection's type argument %s", result.sourceTree(), sourceType, targetType));
}
switch(fixType) {
case PRINT_TYPES_AS_COMMENT:
description.addFix(SuggestedFix.prefixWith(tree, String.format("/* expected: %s, actual: %s */", ASTHelpers.getUpperBound(result.targetType(), types), result.sourceType())));
break;
case CAST:
Fix fix;
if (typeArgResult != null) {
TypeArgOfMethodArgMatcher matcher = (TypeArgOfMethodArgMatcher) typeArgResult.matcher();
String fullyQualifiedType = matcher.getMethodArgTypeName();
String simpleType = Iterables.getLast(Splitter.on('.').split(fullyQualifiedType));
fix = SuggestedFix.builder().prefixWith(result.sourceTree(), String.format("(%s<?>) ", simpleType)).addImport(fullyQualifiedType).build();
} else {
fix = SuggestedFix.prefixWith(result.sourceTree(), "(Object) ");
}
description.addFix(fix);
break;
case SUPPRESS_WARNINGS:
SuggestedFix.Builder builder = SuggestedFix.builder();
builder.prefixWith(result.sourceTree(), String.format("/* expected: %s, actual: %s */ ", targetType, sourceType));
addSuppressWarnings(builder, state, "CollectionIncompatibleType");
description.addFix(builder.build());
break;
case NONE:
break;
}
return description.build();
}
use of com.sun.tools.javac.code.Types 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();
conditionExpr = ASTHelpers.stripParentheses(conditionExpr);
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;
}
}
use of com.sun.tools.javac.code.Types 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.sun.tools.javac.code.Types 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.sun.tools.javac.code.Types 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;
}
Aggregations