• 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 com.google.auto.common.MoreElements.getPackage;
20 import static com.google.common.base.Preconditions.checkArgument;
21 import static javax.lang.model.element.Modifier.PRIVATE;
22 import static javax.lang.model.element.Modifier.PUBLIC;
23 
24 import com.google.auto.common.MoreElements;
25 import java.util.Optional;
26 import javax.lang.model.element.Element;
27 import javax.lang.model.element.ElementKind;
28 import javax.lang.model.element.ExecutableElement;
29 import javax.lang.model.element.PackageElement;
30 import javax.lang.model.element.TypeElement;
31 import javax.lang.model.element.TypeParameterElement;
32 import javax.lang.model.element.VariableElement;
33 import javax.lang.model.type.ArrayType;
34 import javax.lang.model.type.DeclaredType;
35 import javax.lang.model.type.NoType;
36 import javax.lang.model.type.NullType;
37 import javax.lang.model.type.PrimitiveType;
38 import javax.lang.model.type.TypeMirror;
39 import javax.lang.model.type.TypeVariable;
40 import javax.lang.model.type.TypeVisitor;
41 import javax.lang.model.type.WildcardType;
42 import javax.lang.model.util.SimpleElementVisitor6;
43 import javax.lang.model.util.SimpleTypeVisitor6;
44 import javax.lang.model.util.SimpleTypeVisitor8;
45 
46 /**
47  * Utility methods for determining whether a {@linkplain TypeMirror type} or an {@linkplain Element
48  * element} is accessible given the rules outlined in <a
49  * href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6">section 6.6 of the
50  * Java Language Specification</a>.
51  *
52  * <p>This class only provides an approximation for accessibility. It does not always yield the same
53  * result as the compiler, but will always err on the side of declaring something inaccessible. This
54  * ensures that using this class will never result in generating code that will not compile.
55  *
56  * <p>Whenever compiler independence is not a requirement, the compiler-specific implementation of
57  * this functionality should be preferred. For example, {@link
58  * com.sun.source.util.Trees#isAccessible(com.sun.source.tree.Scope, TypeElement)} would be
59  * preferable for {@code javac}.
60  */
61 public final class Accessibility {
62   /** Returns true if the given type can be referenced from any package. */
isTypePubliclyAccessible(TypeMirror type)63   public static boolean isTypePubliclyAccessible(TypeMirror type) {
64     return type.accept(new TypeAccessibilityVisitor(), null);
65   }
66 
67   /** Returns true if the given type can be referenced from code in the given package. */
isTypeAccessibleFrom(TypeMirror type, String packageName)68   public static boolean isTypeAccessibleFrom(TypeMirror type, String packageName) {
69     return type.accept(new TypeAccessibilityVisitor(packageName), null);
70   }
71 
isTypeAccessibleFrom(TypeMirror type, Optional<String> packageName)72   private static boolean isTypeAccessibleFrom(TypeMirror type, Optional<String> packageName) {
73     return type.accept(new TypeAccessibilityVisitor(packageName), null);
74   }
75 
76   private static final class TypeAccessibilityVisitor extends SimpleTypeVisitor6<Boolean, Void> {
77     final Optional<String> packageName;
78 
TypeAccessibilityVisitor()79     TypeAccessibilityVisitor() {
80       this(Optional.empty());
81     }
82 
TypeAccessibilityVisitor(String packageName)83     TypeAccessibilityVisitor(String packageName) {
84       this(Optional.of(packageName));
85     }
86 
TypeAccessibilityVisitor(Optional<String> packageName)87     TypeAccessibilityVisitor(Optional<String> packageName) {
88       this.packageName = packageName;
89     }
90 
isAccessible(TypeMirror type)91     boolean isAccessible(TypeMirror type) {
92       return type.accept(this, null);
93     }
94 
95     @Override
visitNoType(NoType type, Void p)96     public Boolean visitNoType(NoType type, Void p) {
97       return true;
98     }
99 
100     @Override
visitDeclared(DeclaredType type, Void p)101     public Boolean visitDeclared(DeclaredType type, Void p) {
102       if (!isAccessible(type.getEnclosingType())) {
103         // TODO(gak): investigate this check.  see comment in Binding
104         return false;
105       }
106       if (!isElementAccessibleFrom(type.asElement(), packageName)) {
107         return false;
108       }
109       for (TypeMirror typeArgument : type.getTypeArguments()) {
110         if (!isAccessible(typeArgument)) {
111           return false;
112         }
113       }
114       return true;
115     }
116 
117     @Override
visitArray(ArrayType type, Void p)118     public Boolean visitArray(ArrayType type, Void p) {
119       return type.getComponentType().accept(this, null);
120     }
121 
122     @Override
visitPrimitive(PrimitiveType type, Void p)123     public Boolean visitPrimitive(PrimitiveType type, Void p) {
124       return true;
125     }
126 
127     @Override
visitNull(NullType type, Void p)128     public Boolean visitNull(NullType type, Void p) {
129       return true;
130     }
131 
132     @Override
visitTypeVariable(TypeVariable type, Void p)133     public Boolean visitTypeVariable(TypeVariable type, Void p) {
134       // a _reference_ to a type variable is always accessible
135       return true;
136     }
137 
138     @Override
visitWildcard(WildcardType type, Void p)139     public Boolean visitWildcard(WildcardType type, Void p) {
140       if (type.getExtendsBound() != null && !isAccessible(type.getExtendsBound())) {
141         return false;
142       }
143       if (type.getSuperBound() != null && !isAccessible(type.getSuperBound())) {
144         return false;
145       }
146       return true;
147     }
148 
149     @Override
defaultAction(TypeMirror type, Void p)150     protected Boolean defaultAction(TypeMirror type, Void p) {
151       throw new IllegalArgumentException(
152           String.format(
153               "%s of kind %s should not be checked for accessibility", type, type.getKind()));
154     }
155   }
156 
157   /** Returns true if the given element can be referenced from any package. */
isElementPubliclyAccessible(Element element)158   public static boolean isElementPubliclyAccessible(Element element) {
159     return element.accept(new ElementAccessibilityVisitor(), null);
160   }
161 
162   /** Returns true if the given element can be referenced from code in the given package. */
163   // TODO(gak): account for protected
isElementAccessibleFrom(Element element, String packageName)164   public static boolean isElementAccessibleFrom(Element element, String packageName) {
165     return element.accept(new ElementAccessibilityVisitor(packageName), null);
166   }
167 
isElementAccessibleFrom(Element element, Optional<String> packageName)168   private static boolean isElementAccessibleFrom(Element element, Optional<String> packageName) {
169     return element.accept(new ElementAccessibilityVisitor(packageName), null);
170   }
171 
172   /** Returns true if the given element can be referenced from other code in its own package. */
isElementAccessibleFromOwnPackage(Element element)173   public static boolean isElementAccessibleFromOwnPackage(Element element) {
174     return isElementAccessibleFrom(
175         element, MoreElements.getPackage(element).getQualifiedName().toString());
176   }
177 
178   private static final class ElementAccessibilityVisitor
179       extends SimpleElementVisitor6<Boolean, Void> {
180     final Optional<String> packageName;
181 
ElementAccessibilityVisitor()182     ElementAccessibilityVisitor() {
183       this(Optional.empty());
184     }
185 
ElementAccessibilityVisitor(String packageName)186     ElementAccessibilityVisitor(String packageName) {
187       this(Optional.of(packageName));
188     }
189 
ElementAccessibilityVisitor(Optional<String> packageName)190     ElementAccessibilityVisitor(Optional<String> packageName) {
191       this.packageName = packageName;
192     }
193 
194     @Override
visitPackage(PackageElement element, Void p)195     public Boolean visitPackage(PackageElement element, Void p) {
196       return true;
197     }
198 
199     @Override
visitType(TypeElement element, Void p)200     public Boolean visitType(TypeElement element, Void p) {
201       switch (element.getNestingKind()) {
202         case MEMBER:
203           return accessibleMember(element);
204         case TOP_LEVEL:
205           return accessibleModifiers(element);
206         case ANONYMOUS:
207         case LOCAL:
208           return false;
209       }
210       throw new AssertionError();
211     }
212 
accessibleMember(Element element)213     boolean accessibleMember(Element element) {
214       if (!element.getEnclosingElement().accept(this, null)) {
215         return false;
216       }
217       return accessibleModifiers(element);
218     }
219 
accessibleModifiers(Element element)220     boolean accessibleModifiers(Element element) {
221       if (element.getModifiers().contains(PUBLIC)) {
222         return true;
223       } else if (element.getModifiers().contains(PRIVATE)) {
224         return false;
225       } else if (packageName.isPresent()
226           && getPackage(element).getQualifiedName().contentEquals(packageName.get())) {
227         return true;
228       } else {
229         return false;
230       }
231     }
232 
233     @Override
visitTypeParameter(TypeParameterElement element, Void p)234     public Boolean visitTypeParameter(TypeParameterElement element, Void p) {
235       throw new IllegalArgumentException(
236           "It does not make sense to check the accessibility of a type parameter");
237     }
238 
239     @Override
visitExecutable(ExecutableElement element, Void p)240     public Boolean visitExecutable(ExecutableElement element, Void p) {
241       return accessibleMember(element);
242     }
243 
244     @Override
visitVariable(VariableElement element, Void p)245     public Boolean visitVariable(VariableElement element, Void p) {
246       ElementKind kind = element.getKind();
247       checkArgument(kind.isField(), "checking a variable that isn't a field: %s", kind);
248       return accessibleMember(element);
249     }
250   }
251 
252   private static final TypeVisitor<Boolean, Optional<String>> RAW_TYPE_ACCESSIBILITY_VISITOR =
253       new SimpleTypeVisitor8<Boolean, Optional<String>>() {
254         @Override
255         protected Boolean defaultAction(TypeMirror e, Optional<String> requestingPackage) {
256           return isTypeAccessibleFrom(e, requestingPackage);
257         }
258 
259         @Override
260         public Boolean visitDeclared(DeclaredType t, Optional<String> requestingPackage) {
261           return isElementAccessibleFrom(t.asElement(), requestingPackage);
262         }
263       };
264 
265   /** Returns true if the raw type of {@code type} is accessible from the given package. */
isRawTypeAccessible(TypeMirror type, String requestingPackage)266   public static boolean isRawTypeAccessible(TypeMirror type, String requestingPackage) {
267     return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.of(requestingPackage));
268   }
269 
270   /** Returns true if the raw type of {@code type} is accessible from any package. */
isRawTypePubliclyAccessible(TypeMirror type)271   public static boolean isRawTypePubliclyAccessible(TypeMirror type) {
272     return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.empty());
273   }
274 
Accessibility()275   private Accessibility() {}
276 }
277