/** Skips the finally block if the result would be empty. */ @Nullable private JCBlock inlineFinallyBlock(Inliner inliner) throws CouldNotResolveImportException { if (getFinallyBlock() != null) { JCBlock block = getFinallyBlock().inline(inliner); if (!block.getStatements().isEmpty()) { return block; } } return null; }
@Override public void visitAnnotation(JCAnnotation anno) { if (anno.getArguments().isEmpty()) { try { print("@"); printExpr(anno.annotationType); } catch (IOException e) { throw new UncheckedIOException(e); } } else { super.visitAnnotation(anno); } } }.printStat(variableDecl);
@Override public void visitAnnotation(JCAnnotation anno) { if (anno.getArguments().isEmpty()) { try { print("@"); printExpr(anno.annotationType); } catch (IOException e) { // the supertype swallows exceptions too throw new RuntimeException(e); } } else { super.visitAnnotation(anno); } }
@Override public boolean apply(MethodSymbol methodSymbol) { return !methodSymbol.isStatic() && ((methodSymbol.flags() & Flags.SYNTHETIC) == 0) && ((methodSymbol.flags() & Flags.ABSTRACT) == 0) && methodSymbol.getParameters().isEmpty(); } };
private static String functionalInterfaceSignature(VisitorState state, Type type) { Types types = state.getTypes(); if (!maybeFunctionalInterface(type, types, state)) { return Signatures.descriptor(type, types); } Type descriptorType = types.findDescriptorType(type); List<Type> fiparams = descriptorType.getParameterTypes(); // Implicitly typed block-statement-bodied lambdas are potentially compatible with // void-returning and value-returning functional interface types, so we don't consider return // types in general. The except is nullary functional interfaces, since the lambda parameters // will never be implicitly typed. String result = fiparams.isEmpty() ? Signatures.descriptor(descriptorType.getReturnType(), types) : "_"; return String.format( "(%s)->%s", fiparams.stream().map(t -> Signatures.descriptor(t, types)).collect(joining(",")), result); }
private Optional<String> generateFix( MethodInvocationTree methodInvocation, boolean negated, VisitorState state) { String methodName = ASTHelpers.getSymbol(methodInvocation).getQualifiedName().toString(); String hasMethod = methodName.replaceFirst("get", "has"); // proto3 does not generate has methods for scalar types, e.g. ByteString and String. // Do not provide a replacement in these cases. Set<MethodSymbol> hasMethods = ASTHelpers.findMatchingMethods( state.getName(hasMethod), ms -> ms.params().isEmpty(), getType(getReceiver(methodInvocation)), state.getTypes()); if (hasMethods.isEmpty()) { return Optional.empty(); } String replacement = replaceLast(methodInvocation.toString(), methodName, hasMethod); return Optional.of(negated ? ("!" + replacement) : replacement); }
private static boolean isAnnotationOnType(Symbol sym, TypeAnnotationPosition position) { if (!position.location.isEmpty()) { return false; } switch (sym.getKind()) { case LOCAL_VARIABLE: return position.type == TargetType.LOCAL_VARIABLE; case FIELD: return position.type == TargetType.FIELD; case CONSTRUCTOR: case METHOD: return position.type == TargetType.METHOD_RETURN; case PARAMETER: switch (position.type) { case METHOD_FORMAL_PARAMETER: return ((MethodSymbol) sym.owner).getParameters().indexOf(sym) == position.parameter_index; default: return false; } case CLASS: // There are no type annotations on the top-level type of the class being declared, only // on other types in the signature (e.g. `class Foo extends Bar<@A Baz> {}`). return false; default: throw new AssertionError( "unsupported element kind in MoreAnnotation#isAnnotationOnType: " + sym.getKind()); } }
private static Type getComparableTypeArgument(ClassTree tree, VisitorState state) { final Type comparable = state .getTypes() .asSuper(ASTHelpers.getType(tree), state.getSymtab().comparableType.asElement()); if (comparable != null && !comparable.getTypeArguments().isEmpty()) { return Iterables.getOnlyElement(comparable.getTypeArguments()); } return null; } }
@Override public Void visitMethodInvocation(MethodInvocationTree node, Void unused) { if (found[0]) { return null; } if (MOCKITO_MATCHER.matches(node, state)) { Type stubber = ASTHelpers.getReturnType(node); if (!stubber.getTypeArguments().isEmpty() && ASTHelpers.isSameType( getOnlyElement(stubber.getTypeArguments()), type, state)) { found[0] = true; } } return super.visitMethodInvocation(node, null); } },
private Description matchNewClassOrMethodInvocation( MethodSymbol symbol, ImmutableList<Commented<ExpressionTree>> arguments, Tree tree) { if (symbol.getParameters().isEmpty()) { return NO_MATCH; } SuggestedFix.Builder fix = SuggestedFix.builder(); forEachPair( arguments.stream(), Stream.concat( symbol.getParameters().stream(), Stream.iterate(getLast(symbol.getParameters()), x -> x)), (commented, param) -> { ImmutableList<Comment> comments = commented.afterComments().isEmpty() ? commented.beforeComments() : commented.afterComments(); boolean matchStandardForm = !commented.afterComments().isEmpty(); comments.stream() .filter(c -> matchingParamComment(c, param, matchStandardForm)) .findFirst() .ifPresent(c -> fixParamComment(fix, commented, param, c)); }); return fix.isEmpty() ? NO_MATCH : describeMatch(tree, fix.build()); }
private Description validateReceiver( MethodInvocationTree tree, MethodInvocationTree receiver, TypeSymbol type, VisitorState state) { if (receiver.getArguments().size() != 1) { return Description.NO_MATCH; } ExpressionTree argument = getOnlyElement(receiver.getArguments()); Type subjectType = getType(argument); if (isSubtype(subjectType, state.getSymtab().iterableType, state)) { if (subjectType.getTypeArguments().isEmpty()) { return Description.NO_MATCH; } subjectType = getOnlyElement(subjectType.getTypeArguments()); } return subjectType == null || subjectType.tsym.equals(type) || !isSubtype(subjectType, GENERATED_MESSAGE.get(state), state) ? Description.NO_MATCH : describeMatch(tree); } }
private static boolean knownNonNullMethod( MethodSymbol methodSymbol, ClassSymbol clazzSymbol, @Nullable Types types) { if (types == null) { return false; } // Proto getters are not null if (methodSymbol.name.toString().startsWith("get") && methodSymbol.params().isEmpty() && !methodSymbol.isStatic()) { Type type = clazzSymbol.type; while (type != null) { TypeSymbol typeSymbol = type.asElement(); if (typeSymbol == null) { break; } if (typeSymbol .getQualifiedName() .contentEquals("com.google.protobuf.AbstractMessageLite")) { return true; } type = types.supertype(type); } } return false; }
private Description check(Tree tree, List<? extends Tree> arguments, VisitorState state) { Symbol sym = ASTHelpers.getSymbol(tree); if (!(sym instanceof MethodSymbol)) { return Description.NO_MATCH; } MethodSymbol methodSymbol = (MethodSymbol) sym; int expected = methodSymbol.getTypeParameters().size(); int actual = arguments.size(); if (actual <= expected) { return Description.NO_MATCH; } for (MethodSymbol superMethod : ASTHelpers.findSuperMethods(methodSymbol, state.getTypes())) { if (!superMethod.getTypeParameters().isEmpty()) { // Exempt methods that override generic methods to preserve the substitutability of the // two types. return Description.NO_MATCH; } } return describeMatch(tree, buildFix(tree, arguments, state)); }
/** Tokenizes as little of the {@code tree} as possible to ensure we grab all the annotations. */ private static ImmutableList<ErrorProneToken> annotationTokens( Tree tree, VisitorState state, int annotationEnd) { int endPos; if (tree instanceof JCMethodDecl) { JCMethodDecl methodTree = (JCMethodDecl) tree; endPos = methodTree.getBody() == null ? state.getEndPosition(methodTree) : methodTree.getBody().getStartPosition(); } else if (tree instanceof JCVariableDecl) { endPos = ((JCVariableDecl) tree).getType().getStartPosition(); } else if (tree instanceof JCClassDecl) { JCClassDecl classTree = (JCClassDecl) tree; endPos = classTree.getMembers().isEmpty() ? state.getEndPosition(classTree) : classTree.getMembers().get(0).getStartPosition(); } else { throw new AssertionError(); } return ErrorProneTokens.getTokens( state.getSourceCode().subSequence(annotationEnd, endPos).toString(), state.context); }
/** Ignore some common ThreadLocal type arguments that are fine to have per-instance copies of. */ private boolean wellKnownTypeArgument(NewClassTree tree, VisitorState state) { Type type = getType(tree); if (type == null) { return false; } type = state.getTypes().asSuper(type, state.getSymbolFromString("java.lang.ThreadLocal")); if (type == null) { return false; } if (type.getTypeArguments().isEmpty()) { return false; } Type argType = getOnlyElement(type.getTypeArguments()); if (WELL_KNOWN_TYPES.contains(argType.asElement().getQualifiedName().toString())) { return true; } if (isSubtype(argType, state.getTypeFromString("java.text.DateFormat"), state)) { return true; } return false; } }
private String getMethodSignature(Symbol.MethodSymbol method) { // Generate method signature String methodSign = method.enclClass().getQualifiedName().toString() + ":" + (method.isStaticOrInstanceInit() ? "" : getSimpleTypeName(method.getReturnType()) + " ") + method.getSimpleName() + "("; if (!method.getParameters().isEmpty()) { for (Symbol.VarSymbol var : method.getParameters()) { methodSign += getSimpleTypeName(var.type) + ", "; } methodSign = methodSign.substring(0, methodSign.lastIndexOf(',')); } methodSign += ")"; LOG(DEBUG, "DEBUG", "@ method sign: " + methodSign); return methodSign; }
@Override public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { if (!MATCHER.matches(tree, state)) { return NO_MATCH; } Type classType = getType(getOnlyElement(tree.getArguments())); if (classType == null || classType.getTypeArguments().isEmpty()) { return NO_MATCH; } Type type = getUpperBound(getOnlyElement(classType.getTypeArguments()), state.getTypes()); if (isSameType(type, state.getSymtab().annotationType, state)) { return NO_MATCH; } RetentionPolicy retention = state.getTypes().getRetention(type.asElement()); switch (retention) { case RUNTIME: break; case SOURCE: case CLASS: return buildDescription(tree) .setMessage( String.format( "%s; %s has %s retention", message(), type.asElement().getSimpleName(), retention)) .build(); } return NO_MATCH; } }
@Override public Description matchMemberReference(MemberReferenceTree tree, VisitorState state) { Type predicateType = predicateType(ASTHelpers.getType(tree), state); if (predicateType == null) { return NO_MATCH; } Type receiverType = ASTHelpers.getReceiverType(tree); if (tree.getName().contentEquals("equals") && !EqualsIncompatibleType.compatibilityOfTypes(receiverType, predicateType, state) .compatible()) { return buildMessage(receiverType, predicateType, tree); } if (tree.getName().contentEquals("isInstance") && ASTHelpers.isSameType(receiverType, state.getSymtab().classType, state) && !receiverType.getTypeArguments().isEmpty()) { Type argumentType = receiverType.getTypeArguments().get(0); Type upperBound = ASTHelpers.getUpperBound(predicateType, state.getTypes()); if (!EqualsIncompatibleType.compatibilityOfTypes(upperBound, argumentType, state) .compatible()) { return buildMessage(upperBound, argumentType, tree); } } return NO_MATCH; }
@Override public void visitTry(JCTry tree) { if (tree.getResources().isEmpty()) { super.visitTry(tree); return;
static Operand classify(JCTree tree, VisitorState state) { CharSequence source = state.getSourceForNode(tree); if (tree instanceof MethodInvocationTree) { // expr.getClass() -> "expr" MethodInvocationTree receiverInvocation = (MethodInvocationTree) tree; MethodSymbol sym = ASTHelpers.getSymbol(receiverInvocation); if (sym != null) { if (sym.getSimpleName().contentEquals("getClass") && sym.params().isEmpty()) { if (receiverInvocation.getMethodSelect() instanceof IdentifierTree) { // unqualified `getClass()` return Operand.create(Kind.EXPR, state.getSourceForNode(tree), source); } return Operand.create( Kind.GET_CLASS, state.getSourceForNode((JCTree) ASTHelpers.getReceiver(receiverInvocation)), source); } } } else if (tree instanceof MemberSelectTree) { // Foo.class -> "Foo" MemberSelectTree select = (MemberSelectTree) tree; if (select.getIdentifier().contentEquals("class")) { return Operand.create( Kind.LITERAL, state.getSourceForNode((JCTree) select.getExpression()), source); } } return Operand.create(Kind.EXPR, source, source); } }