1 /* 2 * Copyright 2012 Google LLC 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 package com.google.auto.value.processor; 17 18 import static java.util.stream.Collectors.joining; 19 import static java.util.stream.Collectors.toCollection; 20 import static javax.lang.model.element.Modifier.PRIVATE; 21 22 import com.google.auto.common.MoreElements; 23 import com.google.auto.common.MoreTypes; 24 import com.google.auto.value.processor.MissingTypes.MissingTypeException; 25 import com.google.common.collect.ImmutableSortedSet; 26 import java.util.HashMap; 27 import java.util.HashSet; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.Set; 31 import javax.lang.model.element.Name; 32 import javax.lang.model.element.NestingKind; 33 import javax.lang.model.element.QualifiedNameable; 34 import javax.lang.model.element.TypeElement; 35 import javax.lang.model.element.TypeParameterElement; 36 import javax.lang.model.type.ArrayType; 37 import javax.lang.model.type.DeclaredType; 38 import javax.lang.model.type.TypeKind; 39 import javax.lang.model.type.TypeMirror; 40 import javax.lang.model.type.TypeVariable; 41 import javax.lang.model.type.TypeVisitor; 42 import javax.lang.model.type.WildcardType; 43 import javax.lang.model.util.ElementFilter; 44 import javax.lang.model.util.Elements; 45 import javax.lang.model.util.SimpleTypeVisitor8; 46 import javax.lang.model.util.Types; 47 48 /** 49 * Takes a set of types and a package and determines which of those types can be imported, and how 50 * to spell any of the types in the set given those imports. 51 * 52 * @author emcmanus@google.com (Éamonn McManus) 53 */ 54 final class TypeSimplifier { 55 /** 56 * The spelling that should be used to refer to a given class, and an indication of whether it 57 * should be imported. 58 */ 59 private static class Spelling { 60 final String spelling; 61 final boolean importIt; 62 Spelling(String spelling, boolean importIt)63 Spelling(String spelling, boolean importIt) { 64 this.spelling = spelling; 65 this.importIt = importIt; 66 } 67 } 68 69 private final Map<String, Spelling> imports; 70 71 /** 72 * Makes a new simplifier for the given package and set of types. 73 * 74 * @param elementUtils the result of {@code ProcessingEnvironment.getElementUtils()} for the 75 * current annotation processing environment. 76 * @param typeUtils the result of {@code ProcessingEnvironment.getTypeUtils()} for the current 77 * annotation processing environment. 78 * @param packageName the name of the package from which classes will be referenced. Classes that 79 * are in the same package do not need to be imported. 80 * @param types the types that will be referenced. 81 * @param base a base class that the class containing the references will extend. This is needed 82 * because nested classes in that class or one of its ancestors are in scope in the generated 83 * subclass, so a reference to another class with the same name as one of them is ambiguous. 84 * @throws MissingTypeException if one of the input types contains an error (typically, is 85 * undefined). 86 */ TypeSimplifier( Elements elementUtils, Types typeUtils, String packageName, Set<TypeMirror> types, TypeMirror base)87 TypeSimplifier( 88 Elements elementUtils, 89 Types typeUtils, 90 String packageName, 91 Set<TypeMirror> types, 92 TypeMirror base) { 93 Set<TypeMirror> typesPlusBase = new TypeMirrorSet(types); 94 if (base != null) { 95 typesPlusBase.add(base); 96 } 97 Set<TypeMirror> topLevelTypes = topLevelTypes(typeUtils, typesPlusBase); 98 Set<TypeMirror> defined = nonPrivateDeclaredTypes(typeUtils, base); 99 this.imports = findImports(elementUtils, typeUtils, packageName, topLevelTypes, defined); 100 } 101 102 /** 103 * Returns the set of types to import. We import every type that is neither in java.lang nor in 104 * the package containing the AutoValue class, provided that the result refers to the type 105 * unambiguously. For example, if there is a property of type java.util.Map.Entry then we will 106 * import java.util.Map.Entry and refer to the property as Entry. We could also import just 107 * java.util.Map in this case and refer to Map.Entry, but currently we never do that. 108 */ typesToImport()109 ImmutableSortedSet<String> typesToImport() { 110 ImmutableSortedSet.Builder<String> typesToImport = ImmutableSortedSet.naturalOrder(); 111 for (Map.Entry<String, Spelling> entry : imports.entrySet()) { 112 if (entry.getValue().importIt) { 113 typesToImport.add(entry.getKey()); 114 } 115 } 116 return typesToImport.build(); 117 } 118 simplifiedClassName(DeclaredType type)119 String simplifiedClassName(DeclaredType type) { 120 TypeElement typeElement = MoreElements.asType(type.asElement()); 121 TypeElement top = topLevelType(typeElement); 122 // We always want to write a class name starting from the outermost class. For example, 123 // if the type is java.util.Map.Entry then we will import java.util.Map and write Map.Entry. 124 String topString = top.getQualifiedName().toString(); 125 if (imports.containsKey(topString)) { 126 String suffix = typeElement.getQualifiedName().toString().substring(topString.length()); 127 return imports.get(topString).spelling + suffix; 128 } else { 129 return typeElement.getQualifiedName().toString(); 130 } 131 } 132 133 // The actual type parameters of the given type. 134 // If we have @AutoValue abstract class Foo<T extends Something> then the subclass will be 135 // final class AutoValue_Foo<T extends Something> extends Foo<T>. 136 // <T extends Something> is the formal type parameter list and 137 // <T> is the actual type parameter list, which is what this method returns. actualTypeParametersString(TypeElement type)138 static String actualTypeParametersString(TypeElement type) { 139 List<? extends TypeParameterElement> typeParameters = type.getTypeParameters(); 140 if (typeParameters.isEmpty()) { 141 return ""; 142 } else { 143 return typeParameters 144 .stream() 145 .map(e -> e.getSimpleName().toString()) 146 .collect(joining(", ", "<", ">")); 147 } 148 } 149 150 /** Returns the name of the given type, including any enclosing types but not the package. */ classNameOf(TypeElement type)151 static String classNameOf(TypeElement type) { 152 String name = type.getQualifiedName().toString(); 153 String pkgName = packageNameOf(type); 154 return pkgName.isEmpty() ? name : name.substring(pkgName.length() + 1); 155 } 156 topLevelType(TypeElement type)157 private static TypeElement topLevelType(TypeElement type) { 158 while (type.getNestingKind() != NestingKind.TOP_LEVEL) { 159 type = MoreElements.asType(type.getEnclosingElement()); 160 } 161 return type; 162 } 163 164 /** 165 * Returns the name of the package that the given type is in. If the type is in the default 166 * (unnamed) package then the name is the empty string. 167 */ packageNameOf(TypeElement type)168 static String packageNameOf(TypeElement type) { 169 return MoreElements.getPackage(type).getQualifiedName().toString(); 170 } 171 simpleNameOf(String s)172 static String simpleNameOf(String s) { 173 if (s.contains(".")) { 174 return s.substring(s.lastIndexOf('.') + 1); 175 } else { 176 return s; 177 } 178 } 179 180 /** 181 * Given a set of referenced types, works out which of them should be imported and what the 182 * resulting spelling of each one is. 183 * 184 * <p>This method operates on a {@code Set<TypeMirror>} rather than just a {@code Set<String>} 185 * because it is not strictly possible to determine what part of a fully-qualified type name is 186 * the package and what part is the top-level class. For example, {@code java.util.Map.Entry} is a 187 * class called {@code Map.Entry} in a package called {@code java.util} assuming Java conventions 188 * are being followed, but it could theoretically also be a class called {@code Entry} in a 189 * package called {@code java.util.Map}. Since we are operating as part of the compiler, our goal 190 * should be complete correctness, and the only way to achieve that is to operate on the real 191 * representations of types. 192 * 193 * @param codePackageName The name of the package where the class containing these references is 194 * defined. Other classes within the same package do not need to be imported. 195 * @param referenced The complete set of declared types (classes and interfaces) that will be 196 * referenced in the generated code. 197 * @param defined The complete set of declared types (classes and interfaces) that are defined 198 * within the scope of the generated class (i.e. nested somewhere in its superclass chain, or 199 * in its interface set) 200 * @return a map where the keys are fully-qualified types and the corresponding values indicate 201 * whether the type should be imported, and how the type should be spelled in the source code. 202 */ findImports( Elements elementUtils, Types typeUtils, String codePackageName, Set<TypeMirror> referenced, Set<TypeMirror> defined)203 private static Map<String, Spelling> findImports( 204 Elements elementUtils, 205 Types typeUtils, 206 String codePackageName, 207 Set<TypeMirror> referenced, 208 Set<TypeMirror> defined) { 209 Map<String, Spelling> imports = new HashMap<>(); 210 Set<TypeMirror> typesInScope = new TypeMirrorSet(); 211 typesInScope.addAll(referenced); 212 typesInScope.addAll(defined); 213 Set<String> ambiguous = ambiguousNames(typeUtils, typesInScope); 214 for (TypeMirror type : referenced) { 215 TypeElement typeElement = (TypeElement) typeUtils.asElement(type); 216 String fullName = typeElement.getQualifiedName().toString(); 217 String simpleName = typeElement.getSimpleName().toString(); 218 String pkg = packageNameOf(typeElement); 219 boolean importIt; 220 String spelling; 221 if (ambiguous.contains(simpleName)) { 222 importIt = false; 223 spelling = fullName; 224 } else if (pkg.equals("java.lang")) { 225 importIt = false; 226 spelling = javaLangSpelling(elementUtils, codePackageName, typeElement); 227 } else if (pkg.equals(codePackageName)) { 228 importIt = false; 229 spelling = fullName.substring(pkg.isEmpty() ? 0 : pkg.length() + 1); 230 } else { 231 importIt = true; 232 spelling = simpleName; 233 } 234 imports.put(fullName, new Spelling(spelling, importIt)); 235 } 236 return imports; 237 } 238 239 /** 240 * Handles the tricky case where the class being referred to is in {@code java.lang}, but the 241 * package of the referring code contains another class of the same name. For example, if the 242 * current package is {@code foo.bar} and there is a {@code foo.bar.Compiler}, then we will refer 243 * to {@code java.lang.Compiler} by its full name. The plain name {@code Compiler} would reference 244 * {@code foo.bar.Compiler} in this case. We need to write {@code java.lang.Compiler} even if the 245 * other {@code Compiler} class is not being considered here, so the {@link #ambiguousNames} logic 246 * is not enough. We have to look to see if the class exists. 247 */ javaLangSpelling( Elements elementUtils, String codePackageName, TypeElement typeElement)248 private static String javaLangSpelling( 249 Elements elementUtils, String codePackageName, TypeElement typeElement) { 250 // If this is java.lang.Thread.State or the like, we have to look for a clash with Thread. 251 TypeElement topLevelType = topLevelType(typeElement); 252 TypeElement clash = 253 elementUtils.getTypeElement(codePackageName + "." + topLevelType.getSimpleName()); 254 String fullName = typeElement.getQualifiedName().toString(); 255 return (clash == null) ? fullName.substring("java.lang.".length()) : fullName; 256 } 257 258 /** 259 * Finds the top-level types for all the declared types (classes and interfaces) in the given 260 * {@code Set<TypeMirror>}. 261 * 262 * <p>The returned set contains only top-level types. If we reference {@code java.util.Map.Entry} 263 * then the returned set will contain {@code java.util.Map}. This is because we want to write 264 * {@code Map.Entry} everywhere rather than {@code Entry}. 265 */ topLevelTypes(Types typeUtil, Set<TypeMirror> types)266 private static Set<TypeMirror> topLevelTypes(Types typeUtil, Set<TypeMirror> types) { 267 return types 268 .stream() 269 .map(typeMirror -> MoreElements.asType(typeUtil.asElement(typeMirror))) 270 .map(typeElement -> topLevelType(typeElement).asType()) 271 .collect(toCollection(TypeMirrorSet::new)); 272 } 273 274 /** 275 * Finds all types that are declared with non private visibility by the given {@code TypeMirror}, 276 * any class in its superclass chain, or any interface it implements. 277 */ nonPrivateDeclaredTypes(Types typeUtils, TypeMirror type)278 private static Set<TypeMirror> nonPrivateDeclaredTypes(Types typeUtils, TypeMirror type) { 279 if (type == null) { 280 return new TypeMirrorSet(); 281 } else { 282 Set<TypeMirror> declared = new TypeMirrorSet(); 283 declared.add(type); 284 List<TypeElement> nestedTypes = 285 ElementFilter.typesIn(typeUtils.asElement(type).getEnclosedElements()); 286 for (TypeElement nestedType : nestedTypes) { 287 if (!nestedType.getModifiers().contains(PRIVATE)) { 288 declared.add(nestedType.asType()); 289 } 290 } 291 for (TypeMirror supertype : typeUtils.directSupertypes(type)) { 292 declared.addAll(nonPrivateDeclaredTypes(typeUtils, supertype)); 293 } 294 return declared; 295 } 296 } 297 ambiguousNames(Types typeUtils, Set<TypeMirror> types)298 private static Set<String> ambiguousNames(Types typeUtils, Set<TypeMirror> types) { 299 Set<String> ambiguous = new HashSet<>(); 300 Map<String, Name> simpleNamesToQualifiedNames = new HashMap<>(); 301 for (TypeMirror type : types) { 302 if (type.getKind() == TypeKind.ERROR) { 303 throw new MissingTypeException(MoreTypes.asError(type)); 304 } 305 String simpleName = typeUtils.asElement(type).getSimpleName().toString(); 306 /* 307 * Compare by qualified names, because in Eclipse JDT, if Java 8 type annotations are used, 308 * the same (unannotated) type may appear multiple times in the Set<TypeMirror>. 309 * TODO(emcmanus): investigate further, because this might cause problems elsewhere. 310 */ 311 Name qualifiedName = ((QualifiedNameable) typeUtils.asElement(type)).getQualifiedName(); 312 Name previous = simpleNamesToQualifiedNames.put(simpleName, qualifiedName); 313 if (previous != null && !previous.equals(qualifiedName)) { 314 ambiguous.add(simpleName); 315 } 316 } 317 return ambiguous; 318 } 319 320 /** 321 * Returns true if casting to the given type will elicit an unchecked warning from the compiler. 322 * Only generic types such as {@code List<String>} produce such warnings. There will be no warning 323 * if the type's only generic parameters are simple wildcards, as in {@code Map<?, ?>}. 324 */ isCastingUnchecked(TypeMirror type)325 static boolean isCastingUnchecked(TypeMirror type) { 326 return CASTING_UNCHECKED_VISITOR.visit(type, null); 327 } 328 329 private static final TypeVisitor<Boolean, Void> CASTING_UNCHECKED_VISITOR = 330 new SimpleTypeVisitor8<Boolean, Void>(false) { 331 @Override 332 public Boolean visitUnknown(TypeMirror t, Void p) { 333 // We don't know whether casting is unchecked for this mysterious type but assume it is, 334 // so we will insert a possibly unnecessary @SuppressWarnings("unchecked"). 335 return true; 336 } 337 338 @Override 339 public Boolean visitArray(ArrayType t, Void p) { 340 return visit(t.getComponentType(), p); 341 } 342 343 @Override 344 public Boolean visitDeclared(DeclaredType t, Void p) { 345 return t.getTypeArguments().stream().anyMatch(TypeSimplifier::uncheckedTypeArgument); 346 } 347 348 @Override 349 public Boolean visitTypeVariable(TypeVariable t, Void p) { 350 return true; 351 } 352 }; 353 354 // If a type has a type argument, then casting to the type is unchecked, except if the argument 355 // is <?> or <? extends Object>. The same applies to all type arguments, so casting to Map<?, ?> 356 // does not produce an unchecked warning for example. uncheckedTypeArgument(TypeMirror arg)357 private static boolean uncheckedTypeArgument(TypeMirror arg) { 358 if (arg.getKind() == TypeKind.WILDCARD) { 359 WildcardType wildcard = (WildcardType) arg; 360 if (wildcard.getExtendsBound() == null || isJavaLangObject(wildcard.getExtendsBound())) { 361 // This is <?>, unless there's a super bound, in which case it is <? super Foo> and 362 // is erased. 363 return (wildcard.getSuperBound() != null); 364 } 365 } 366 return true; 367 } 368 isJavaLangObject(TypeMirror type)369 private static boolean isJavaLangObject(TypeMirror type) { 370 if (type.getKind() != TypeKind.DECLARED) { 371 return false; 372 } 373 DeclaredType declaredType = (DeclaredType) type; 374 TypeElement typeElement = (TypeElement) declaredType.asElement(); 375 return typeElement.getQualifiedName().contentEquals("java.lang.Object"); 376 } 377 } 378