diff --git a/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java b/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java index 3ee45d37976..e8dc144b202 100644 --- a/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java +++ b/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java @@ -31,7 +31,6 @@ import com.google.auto.value.AutoValue; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher; -import com.google.common.base.Predicate; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -133,7 +132,6 @@ import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.FatalError; -import com.sun.tools.javac.util.Filter; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler; import com.sun.tools.javac.util.Name; @@ -153,6 +151,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nullable; @@ -654,11 +653,8 @@ private static Stream findSuperMethods( * in an unspecified order. */ public static Stream matchingMethods( - Name name, - java.util.function.Predicate predicate, - Type startClass, - Types types) { - Filter matchesMethodPredicate = + Name name, Predicate predicate, Type startClass, Types types) { + Predicate matchesMethodPredicate = sym -> sym instanceof MethodSymbol && predicate.test((MethodSymbol) sym); // Iterate over all classes and interfaces that startClass inherits from. @@ -672,8 +668,8 @@ public static Stream matchingMethods( return Stream.empty(); } return Streams.stream( - superClassSymbols.getSymbolsByName( - name, matchesMethodPredicate, NON_RECURSIVE)) + scope(superClassSymbols) + .getSymbolsByName(name, matchesMethodPredicate, NON_RECURSIVE)) // By definition of the filter, we know that the symbol is a MethodSymbol. .map(symbol -> (MethodSymbol) symbol); }); @@ -2179,5 +2175,10 @@ public static boolean isLocal(Symbol symbol) { } } + /** Returns a compatibility adapter around {@link Scope}. */ + public static ErrorProneScope scope(Scope scope) { + return new ErrorProneScope(scope); + } + private ASTHelpers() {} } diff --git a/check_api/src/main/java/com/google/errorprone/util/ErrorProneScope.java b/check_api/src/main/java/com/google/errorprone/util/ErrorProneScope.java new file mode 100644 index 00000000000..4ba4edfe750 --- /dev/null +++ b/check_api/src/main/java/com/google/errorprone/util/ErrorProneScope.java @@ -0,0 +1,123 @@ +/* + * Copyright 2021 The Error Prone Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.errorprone.util; + +import static com.google.common.base.Preconditions.checkState; + +import com.sun.tools.javac.code.Scope; +import com.sun.tools.javac.code.Scope.LookupKind; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.util.Name; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.function.Predicate; + +/** A compatibility wrapper around {@link com.sun.tools.javac.util.Filter} */ +public final class ErrorProneScope { + + @SuppressWarnings("unchecked") // reflection + public Iterable getSymbolsByName(Name name, Predicate predicate) { + return (Iterable) invoke(getSymbolsByName, name, maybeAsFilter(predicate)); + } + + @SuppressWarnings("unchecked") // reflection + public Iterable getSymbolsByName( + Name name, Predicate predicate, LookupKind lookupKind) { + return (Iterable) + invoke(getSymbolsByNameLookupKind, name, maybeAsFilter(predicate), lookupKind); + } + + @SuppressWarnings("unchecked") // reflection + public Iterable getSymbols(Predicate predicate) { + return (Iterable) invoke(getSymbols, maybeAsFilter(predicate)); + } + + public boolean anyMatch(Predicate predicate) { + return (boolean) invoke(anyMatch, maybeAsFilter(predicate)); + } + + private static final Class filterClass = getFilterClass(); + + private static Class getFilterClass() { + try { + return Class.forName("com.sun.tools.javac.util.Filter"); + } catch (ClassNotFoundException e) { + return null; + } + } + + private static final Method anyMatch = getImpl("anyMatch", Predicate.class); + + private static final Method getSymbolsByName = + getImpl("getSymbolsByName", Name.class, Predicate.class); + + private static final Method getSymbolsByNameLookupKind = + getImpl("getSymbolsByName", Name.class, Predicate.class, LookupKind.class); + + private static final Method getSymbols = getImpl("getSymbols", Predicate.class); + + private static Method getImpl(String name, Class... parameters) { + return filterClass != null + ? getMethodOrDie( + Scope.class, + name, + Arrays.stream(parameters) + .map(p -> p.equals(Predicate.class) ? filterClass : p) + .toArray(Class[]::new)) + : getMethodOrDie(Scope.class, name, parameters); + } + + private final Scope scope; + + ErrorProneScope(Scope scope) { + this.scope = scope; + } + + private Object invoke(Method method, Object... args) { + try { + return method.invoke(scope, args); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); + } + } + + private Object maybeAsFilter(Predicate predicate) { + if (filterClass == null) { + return predicate; + } + return Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] {filterClass}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + checkState(method.getName().equals("accepts")); + return predicate.test((Symbol) args[0]); + } + }); + } + + private static Method getMethodOrDie(Class clazz, String name, Class... parameters) { + try { + return clazz.getMethod(name, parameters); + } catch (NoSuchMethodException e) { + throw new LinkageError(e.getMessage(), e); + } + } +} diff --git a/check_api/src/main/java/com/google/errorprone/util/FindIdentifiers.java b/check_api/src/main/java/com/google/errorprone/util/FindIdentifiers.java index b00d82acfc0..d1b98efa91e 100644 --- a/check_api/src/main/java/com/google/errorprone/util/FindIdentifiers.java +++ b/check_api/src/main/java/com/google/errorprone/util/FindIdentifiers.java @@ -203,7 +203,7 @@ public static ImmutableSet findAllIdents(VisitorState state) { for (Type type : superTypes) { Scope scope = type.tsym.members(); ImmutableList.Builder varsList = ImmutableList.builder(); - for (Symbol var : scope.getSymbols(VarSymbol.class::isInstance)) { + for (Symbol var : ASTHelpers.scope(scope).getSymbols(VarSymbol.class::isInstance)) { varsList.add((VarSymbol) var); } result.addAll(varsList.build().reverse()); @@ -250,10 +250,12 @@ public static ImmutableSet findAllIdents(VisitorState state) { ASTHelpers.getType(memberSelectTree.getExpression()), /* skipInterface= */ false); for (Symbol var : - scope.getSymbols( - sym -> - sym instanceof VarSymbol - && sym.getSimpleName().equals(memberSelectTree.getIdentifier()))) { + ASTHelpers.scope(scope) + .getSymbols( + sym -> + sym instanceof VarSymbol + && sym.getSimpleName() + .equals(memberSelectTree.getIdentifier()))) { result.add((VarSymbol) var); } } @@ -330,7 +332,8 @@ public static ImmutableList findAllFields(Type classType, VisitorStat if (scope == null) { return ImmutableList.of().stream(); } - return ImmutableList.copyOf(scope.getSymbols(VarSymbol.class::isInstance)) + return ImmutableList.copyOf( + ASTHelpers.scope(scope).getSymbols(VarSymbol.class::isInstance)) .reverse() .stream() .map(v -> (VarSymbol) v) diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/JavaLangClash.java b/core/src/main/java/com/google/errorprone/bugpatterns/JavaLangClash.java index 084b6a5effa..08331a2b935 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/JavaLangClash.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/JavaLangClash.java @@ -63,7 +63,8 @@ private Description check(Tree tree, Name simpleName, VisitorState state) { PackageSymbol javaLang = symtab.enterPackage(symtab.java_base, state.getNames().java_lang); Symbol other = getFirst( - javaLang.members().getSymbolsByName(simpleName, s -> s.getModifiers().contains(PUBLIC)), + ASTHelpers.scope(javaLang.members()) + .getSymbolsByName(simpleName, s -> s.getModifiers().contains(PUBLIC)), null); Symbol symbol = ASTHelpers.getSymbol(tree); if (other == null || other.equals(symbol)) { diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/ObjectToString.java b/core/src/main/java/com/google/errorprone/bugpatterns/ObjectToString.java index 16012cf6338..9a455a412be 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/ObjectToString.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/ObjectToString.java @@ -24,6 +24,7 @@ import com.google.errorprone.fixes.Fix; import com.google.errorprone.fixes.SuggestedFixes; import com.google.errorprone.predicates.TypePredicate; +import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.Tree; import com.sun.tools.javac.code.Symbol.MethodSymbol; @@ -64,8 +65,7 @@ private static boolean finalNoOverrides(Type type, VisitorState state) { // implements and we collect all overrides of java.lang.Object.toString(). If one of those // overrides is present, then we don't flag it. return Iterables.isEmpty( - types - .membersClosure(type, /* skipInterface= */ false) + ASTHelpers.scope(types.membersClosure(type, /* skipInterface= */ false)) .getSymbolsByName( names.toString, m -> diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/RestrictedApiChecker.java b/core/src/main/java/com/google/errorprone/bugpatterns/RestrictedApiChecker.java index f8fc2415d0e..a0f7c6269a8 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/RestrictedApiChecker.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/RestrictedApiChecker.java @@ -163,8 +163,7 @@ private static MethodSymbol superclassConstructorSymbol(NewClassTree tree, Visit return (MethodSymbol) getOnlyElement( - superclass - .members() + ASTHelpers.scope(superclass.members()) .getSymbols( member -> member.isConstructor() diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/UnnecessaryMethodReference.java b/core/src/main/java/com/google/errorprone/bugpatterns/UnnecessaryMethodReference.java index c701d46c65d..fcfa6f7d2e6 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/UnnecessaryMethodReference.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/UnnecessaryMethodReference.java @@ -34,13 +34,14 @@ import com.google.errorprone.matchers.Matcher; import com.google.errorprone.predicates.TypePredicate; import com.google.errorprone.predicates.TypePredicates; +import com.google.errorprone.util.ASTHelpers; import com.google.errorprone.util.ASTHelpers.TargetType; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberReferenceTree; import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.source.tree.MethodInvocationTree; -import com.sun.tools.javac.code.Scope.WriteableScope; +import com.sun.tools.javac.code.Scope; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Type; @@ -77,9 +78,9 @@ public Description matchMemberReference(MemberReferenceTree tree, VisitorState s return NO_MATCH; } MethodSymbol symbol = getSymbol(tree); - WriteableScope members = targetType.type().tsym.members(); - if (!members.anyMatch( - sym -> isFunctionalInterfaceInvocation(symbol, targetType.type(), sym, state)) + Scope members = targetType.type().tsym.members(); + if (!ASTHelpers.scope(members) + .anyMatch(sym -> isFunctionalInterfaceInvocation(symbol, targetType.type(), sym, state)) && !isKnownAlias(tree, targetType.type(), state)) { return NO_MATCH; } diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/UnsafeFinalization.java b/core/src/main/java/com/google/errorprone/bugpatterns/UnsafeFinalization.java index 58d5f41eebf..8aee189a00d 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/UnsafeFinalization.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/UnsafeFinalization.java @@ -146,7 +146,8 @@ private static Symbol getFinalizer(VisitorState state, ClassSymbol enclosing) { } private static Stream getFields(TypeSymbol s) { - return Streams.stream(s.members().getSymbols(m -> m.getKind() == ElementKind.FIELD)) + return Streams.stream( + ASTHelpers.scope(s.members()).getSymbols(m -> m.getKind() == ElementKind.FIELD)) .map(VarSymbol.class::cast); } } diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ImmutableAnalysis.java b/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ImmutableAnalysis.java index c7fe76765a5..9f4817bce93 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ImmutableAnalysis.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ImmutableAnalysis.java @@ -38,13 +38,13 @@ import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ClassType; -import com.sun.tools.javac.util.Filter; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.type.TypeKind; @@ -229,13 +229,8 @@ Violation areFieldsImmutable( if (classSym.members() == null) { return Violation.absent(); } - Filter instanceFieldFilter = - new Filter() { - @Override - public boolean accepts(Symbol symbol) { - return symbol.getKind() == ElementKind.FIELD && !symbol.isStatic(); - } - }; + Predicate instanceFieldFilter = + symbol -> symbol.getKind() == ElementKind.FIELD && !symbol.isStatic(); Map declarations = new HashMap<>(); if (tree.isPresent()) { for (Tree member : tree.get().getMembers()) { @@ -248,7 +243,8 @@ public boolean accepts(Symbol symbol) { // javac gives us members in reverse declaration order // handling them in declaration order leads to marginally better diagnostics List members = - ImmutableList.copyOf(classSym.members().getSymbols(instanceFieldFilter)).reverse(); + ImmutableList.copyOf(ASTHelpers.scope(classSym.members()).getSymbols(instanceFieldFilter)) + .reverse(); for (Symbol member : members) { Optional memberTree = Optional.ofNullable(declarations.get(member)); Violation info = diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/time/PreferJavaTimeOverload.java b/core/src/main/java/com/google/errorprone/bugpatterns/time/PreferJavaTimeOverload.java index 83bbb722666..255f7bab6b3 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/time/PreferJavaTimeOverload.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/time/PreferJavaTimeOverload.java @@ -57,7 +57,6 @@ import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Types; -import com.sun.tools.javac.util.Filter; import com.sun.tools.javac.util.Name; import java.util.List; import java.util.Map; @@ -400,7 +399,7 @@ private static boolean hasTimeSourceMethod(MethodInvocationTree tree, VisitorSta // Adapted from ASTHelpers.findMatchingMethods(); but this short-circuits private static boolean hasMatchingMethods( Name name, final Predicate predicate, Type startClass, Types types) { - Filter matchesMethodPredicate = + Predicate matchesMethodPredicate = sym -> sym instanceof MethodSymbol && predicate.apply((MethodSymbol) sym); // Iterate over all classes and interfaces that startClass inherits from. @@ -410,7 +409,8 @@ private static boolean hasMatchingMethods( Scope superClassSymbols = superClassSymbol.members(); if (superClassSymbols != null) { // Can be null if superClass is a type variable if (!Iterables.isEmpty( - superClassSymbols.getSymbolsByName(name, matchesMethodPredicate, NON_RECURSIVE))) { + ASTHelpers.scope(superClassSymbols) + .getSymbolsByName(name, matchesMethodPredicate, NON_RECURSIVE))) { return true; } }