• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen.langmodel;
18 
19 import static androidx.room.compiler.processing.compat.XConverters.toJavac;
20 import static com.google.auto.common.MoreElements.getPackage;
21 import static com.google.auto.common.MoreTypes.asElement;
22 import static com.google.common.base.Preconditions.checkArgument;
23 import static javax.lang.model.element.Modifier.PRIVATE;
24 import static javax.lang.model.element.Modifier.PROTECTED;
25 import static javax.lang.model.element.Modifier.PUBLIC;
26 
27 import androidx.room.compiler.processing.XElement;
28 import androidx.room.compiler.processing.XType;
29 import androidx.room.compiler.processing.XTypeElement;
30 import java.util.Optional;
31 import javax.lang.model.element.Element;
32 import javax.lang.model.element.ElementKind;
33 import javax.lang.model.element.ExecutableElement;
34 import javax.lang.model.element.PackageElement;
35 import javax.lang.model.element.TypeElement;
36 import javax.lang.model.element.TypeParameterElement;
37 import javax.lang.model.element.VariableElement;
38 import javax.lang.model.type.ArrayType;
39 import javax.lang.model.type.DeclaredType;
40 import javax.lang.model.type.NoType;
41 import javax.lang.model.type.NullType;
42 import javax.lang.model.type.PrimitiveType;
43 import javax.lang.model.type.TypeKind;
44 import javax.lang.model.type.TypeMirror;
45 import javax.lang.model.type.TypeVariable;
46 import javax.lang.model.type.WildcardType;
47 import javax.lang.model.util.SimpleElementVisitor8;
48 import javax.lang.model.util.SimpleTypeVisitor8;
49 
50 /**
51  * Utility methods for determining whether a {@linkplain TypeMirror type} or an {@linkplain Element
52  * element} is accessible given the rules outlined in <a
53  * href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6">section 6.6 of the
54  * Java Language Specification</a>.
55  *
56  * <p>This class only provides an approximation for accessibility. It does not always yield the same
57  * result as the compiler, but will always err on the side of declaring something inaccessible. This
58  * ensures that using this class will never result in generating code that will not compile.
59  *
60  * <p>Whenever compiler independence is not a requirement, the compiler-specific implementation of
61  * this functionality should be preferred. For example, {@link
62  * com.sun.source.util.Trees#isAccessible(com.sun.source.tree.Scope, TypeElement)} would be
63  * preferable for {@code javac}.
64  */
65 public final class Accessibility {
66   /** Returns true if the given type can be referenced from any package. */
isTypePubliclyAccessible(TypeMirror type)67   public static boolean isTypePubliclyAccessible(TypeMirror type) {
68     return type.accept(new TypeAccessibilityVisitor(Optional.empty()), null);
69   }
70 
71   /** Returns true if the given type can be referenced from code in the given package. */
isTypeAccessibleFrom(XType type, String packageName)72   public static boolean isTypeAccessibleFrom(XType type, String packageName) {
73     return isTypeAccessibleFrom(toJavac(type), packageName);
74   }
75 
76   /** Returns true if the given type can be referenced from code in the given package. */
isTypeAccessibleFrom(TypeMirror type, String packageName)77   public static boolean isTypeAccessibleFrom(TypeMirror type, String packageName) {
78     return type.accept(new TypeAccessibilityVisitor(Optional.of(packageName)), null);
79   }
80 
81   /**
82    * Returns true if the given type is protected and can be referenced from the given requesting
83    * element.
84    */
isProtectedMemberOf(DeclaredType type, XTypeElement requestingElement)85   public static boolean isProtectedMemberOf(DeclaredType type, XTypeElement requestingElement) {
86     return isProtectedAccessibleFromElement(type.asElement(), requestingElement);
87   }
88 
isProtectedAccessibleFromElement( Element element, XTypeElement requestingElement)89   private static Boolean isProtectedAccessibleFromElement(
90       Element element, XTypeElement requestingElement) {
91     if (!element.getModifiers().contains(PROTECTED)) {
92       return false;
93     }
94     if (element.getEnclosingElement().equals(toJavac(requestingElement))) {
95       return true;
96     }
97     // Check if the element is protected member of the requesting element's super class.
98     if (requestingElement.getSuperType() != null) {
99       return isProtectedAccessibleFromElement(
100           element, requestingElement.getSuperType().getTypeElement());
101     }
102     return false;
103   }
104 
105   private static final class TypeAccessibilityVisitor extends SimpleTypeVisitor8<Boolean, Void> {
106     private final Optional<String> packageName;
107 
TypeAccessibilityVisitor(Optional<String> packageName)108     TypeAccessibilityVisitor(Optional<String> packageName) {
109       this.packageName = packageName;
110     }
111 
isAccessible(TypeMirror type)112     boolean isAccessible(TypeMirror type) {
113       return type.accept(this, null);
114     }
115 
116     @Override
visitNoType(NoType type, Void p)117     public Boolean visitNoType(NoType type, Void p) {
118       return true;
119     }
120 
121     @Override
visitDeclared(DeclaredType type, Void p)122     public Boolean visitDeclared(DeclaredType type, Void p) {
123       if (!isAccessible(type.getEnclosingType())) {
124         // TODO(gak): investigate this check.  see comment in Binding
125         return false;
126       }
127       if (!type.asElement().accept(new ElementAccessibilityVisitor(packageName), null)) {
128         return false;
129       }
130       for (TypeMirror typeArgument : type.getTypeArguments()) {
131         if (!isAccessible(typeArgument)) {
132           return false;
133         }
134       }
135       return true;
136     }
137 
138     @Override
visitArray(ArrayType type, Void p)139     public Boolean visitArray(ArrayType type, Void p) {
140       return type.getComponentType().accept(this, null);
141     }
142 
143     @Override
visitPrimitive(PrimitiveType type, Void p)144     public Boolean visitPrimitive(PrimitiveType type, Void p) {
145       return true;
146     }
147 
148     @Override
visitNull(NullType type, Void p)149     public Boolean visitNull(NullType type, Void p) {
150       return true;
151     }
152 
153     @Override
visitTypeVariable(TypeVariable type, Void p)154     public Boolean visitTypeVariable(TypeVariable type, Void p) {
155       // a _reference_ to a type variable is always accessible
156       return true;
157     }
158 
159     @Override
visitWildcard(WildcardType type, Void p)160     public Boolean visitWildcard(WildcardType type, Void p) {
161       if (type.getExtendsBound() != null && !isAccessible(type.getExtendsBound())) {
162         return false;
163       }
164       if (type.getSuperBound() != null && !isAccessible(type.getSuperBound())) {
165         return false;
166       }
167       return true;
168     }
169 
170     @Override
defaultAction(TypeMirror type, Void p)171     protected Boolean defaultAction(TypeMirror type, Void p) {
172       throw new IllegalArgumentException(
173           String.format(
174               "%s of kind %s should not be checked for accessibility", type, type.getKind()));
175     }
176   }
177 
178   /** Returns true if the given element can be referenced from any package. */
isElementPubliclyAccessible(Element element)179   public static boolean isElementPubliclyAccessible(Element element) {
180     return element.accept(new ElementAccessibilityVisitor(Optional.empty()), null);
181   }
182 
183   /** Returns true if the given element can be referenced from code in the given package. */
184   // TODO(gak): account for protected
185   // TODO(bcorso): account for kotlin srcs (package-private doesn't exist, internal does exist).
isElementAccessibleFrom(XElement element, String packageName)186   public static boolean isElementAccessibleFrom(XElement element, String packageName) {
187     return isElementAccessibleFrom(toJavac(element), packageName);
188   }
189 
190   /** Returns true if the given element can be referenced from code in the given package. */
191   // TODO(gak): account for protected
isElementAccessibleFrom(Element element, String packageName)192   public static boolean isElementAccessibleFrom(Element element, String packageName) {
193     return element.accept(new ElementAccessibilityVisitor(Optional.of(packageName)), null);
194   }
195 
196   /** Returns true if the given element can be referenced from other code in its own package. */
isElementAccessibleFromOwnPackage(XElement element)197   public static boolean isElementAccessibleFromOwnPackage(XElement element) {
198     return isElementAccessibleFromOwnPackage(toJavac(element));
199   }
200 
201   /** Returns true if the given element can be referenced from other code in its own package. */
isElementAccessibleFromOwnPackage(Element element)202   public static boolean isElementAccessibleFromOwnPackage(Element element) {
203     return isElementAccessibleFrom(element, getPackage(element).getQualifiedName().toString());
204   }
205 
206   private static final class ElementAccessibilityVisitor
207       extends SimpleElementVisitor8<Boolean, Void> {
208     private final Optional<String> packageName;
209 
ElementAccessibilityVisitor(Optional<String> packageName)210     ElementAccessibilityVisitor(Optional<String> packageName) {
211       this.packageName = packageName;
212     }
213 
214     @Override
visitPackage(PackageElement element, Void p)215     public Boolean visitPackage(PackageElement element, Void p) {
216       return true;
217     }
218 
219     @Override
visitType(TypeElement element, Void p)220     public Boolean visitType(TypeElement element, Void p) {
221       switch (element.getNestingKind()) {
222         case MEMBER:
223           return accessibleMember(element);
224         case TOP_LEVEL:
225           return accessibleModifiers(element);
226         case ANONYMOUS:
227         case LOCAL:
228           return false;
229       }
230       throw new AssertionError();
231     }
232 
accessibleMember(Element element)233     boolean accessibleMember(Element element) {
234       return element.getEnclosingElement().accept(this, null) && accessibleModifiers(element);
235     }
236 
accessibleModifiers(Element element)237     boolean accessibleModifiers(Element element) {
238       if (element.getModifiers().contains(PUBLIC)) {
239         return true;
240       } else if (element.getModifiers().contains(PRIVATE)) {
241         return false;
242       }
243       return packageName.isPresent()
244           && getPackage(element).getQualifiedName().contentEquals(packageName.get());
245     }
246 
247     @Override
visitTypeParameter(TypeParameterElement element, Void p)248     public Boolean visitTypeParameter(TypeParameterElement element, Void p) {
249       throw new IllegalArgumentException(
250           "It does not make sense to check the accessibility of a type parameter");
251     }
252 
253     @Override
visitExecutable(ExecutableElement element, Void p)254     public Boolean visitExecutable(ExecutableElement element, Void p) {
255       return accessibleMember(element);
256     }
257 
258     @Override
visitVariable(VariableElement element, Void p)259     public Boolean visitVariable(VariableElement element, Void p) {
260       ElementKind kind = element.getKind();
261       checkArgument(kind.isField(), "checking a variable that isn't a field: %s", kind);
262       return accessibleMember(element);
263     }
264   }
265 
266   /** Returns true if the raw type of {@code type} is accessible from the given package. */
isRawTypeAccessible(TypeMirror type, String requestingPackage)267   public static boolean isRawTypeAccessible(TypeMirror type, String requestingPackage) {
268     return type.getKind() == TypeKind.DECLARED
269         ? isElementAccessibleFrom(asElement(type), requestingPackage)
270         : isTypeAccessibleFrom(type, requestingPackage);
271   }
272 
273   /** Returns true if the raw type of {@code type} is accessible from any package. */
isRawTypePubliclyAccessible(TypeMirror type)274   public static boolean isRawTypePubliclyAccessible(TypeMirror type) {
275     return type.getKind() == TypeKind.DECLARED
276         ? isElementPubliclyAccessible(asElement(type))
277         : isTypePubliclyAccessible(type);
278   }
279 
Accessibility()280   private Accessibility() {}
281 }
282