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