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