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.stream() 144 .map(e -> e.getSimpleName().toString()) 145 .collect(joining(", ", "<", ">")); 146 } 147 } 148 149 /** Returns the name of the given type, including any enclosing types but not the package. */ classNameOf(TypeElement type)150 static String classNameOf(TypeElement type) { 151 String name = type.getQualifiedName().toString(); 152 String pkgName = packageNameOf(type); 153 return pkgName.isEmpty() ? name : name.substring(pkgName.length() + 1); 154 } 155 topLevelType(TypeElement type)156 private static TypeElement topLevelType(TypeElement type) { 157 while (type.getNestingKind() != NestingKind.TOP_LEVEL) { 158 type = MoreElements.asType(type.getEnclosingElement()); 159 } 160 return type; 161 } 162 163 /** 164 * Returns the name of the package that the given type is in. If the type is in the default 165 * (unnamed) package then the name is the empty string. 166 */ packageNameOf(TypeElement type)167 static String packageNameOf(TypeElement type) { 168 return MoreElements.getPackage(type).getQualifiedName().toString(); 169 } 170 simpleNameOf(String s)171 static String simpleNameOf(String s) { 172 if (s.contains(".")) { 173 return s.substring(s.lastIndexOf('.') + 1); 174 } else { 175 return s; 176 } 177 } 178 179 /** 180 * Given a set of referenced types, works out which of them should be imported and what the 181 * resulting spelling of each one is. 182 * 183 * <p>This method operates on a {@code Set<TypeMirror>} rather than just a {@code Set<String>} 184 * because it is not strictly possible to determine what part of a fully-qualified type name is 185 * the package and what part is the top-level class. For example, {@code java.util.Map.Entry} is a 186 * class called {@code Map.Entry} in a package called {@code java.util} assuming Java conventions 187 * are being followed, but it could theoretically also be a class called {@code Entry} in a 188 * package called {@code java.util.Map}. Since we are operating as part of the compiler, our goal 189 * should be complete correctness, and the only way to achieve that is to operate on the real 190 * representations of types. 191 * 192 * @param codePackageName The name of the package where the class containing these references is 193 * defined. Other classes within the same package do not need to be imported. 194 * @param referenced The complete set of declared types (classes and interfaces) that will be 195 * referenced in the generated code. 196 * @param defined The complete set of declared types (classes and interfaces) that are defined 197 * within the scope of the generated class (i.e. nested somewhere in its superclass chain, or 198 * in its interface set) 199 * @return a map where the keys are fully-qualified types and the corresponding values indicate 200 * whether the type should be imported, and how the type should be spelled in the source code. 201 */ findImports( Elements elementUtils, Types typeUtils, String codePackageName, Set<TypeMirror> referenced, Set<TypeMirror> defined)202 private static Map<String, Spelling> findImports( 203 Elements elementUtils, 204 Types typeUtils, 205 String codePackageName, 206 Set<TypeMirror> referenced, 207 Set<TypeMirror> defined) { 208 Map<String, Spelling> imports = new HashMap<>(); 209 Set<TypeMirror> typesInScope = new TypeMirrorSet(); 210 typesInScope.addAll(referenced); 211 typesInScope.addAll(defined); 212 Set<String> ambiguous = ambiguousNames(typeUtils, typesInScope); 213 for (TypeMirror type : referenced) { 214 TypeElement typeElement = (TypeElement) typeUtils.asElement(type); 215 String fullName = typeElement.getQualifiedName().toString(); 216 String simpleName = typeElement.getSimpleName().toString(); 217 String pkg = packageNameOf(typeElement); 218 boolean importIt; 219 String spelling; 220 if (ambiguous.contains(simpleName)) { 221 importIt = false; 222 spelling = fullName; 223 } else if (pkg.equals("java.lang")) { 224 importIt = false; 225 spelling = javaLangSpelling(elementUtils, codePackageName, typeElement); 226 } else if (pkg.equals(codePackageName)) { 227 importIt = false; 228 spelling = fullName.substring(pkg.isEmpty() ? 0 : pkg.length() + 1); 229 } else { 230 importIt = true; 231 spelling = simpleName; 232 } 233 imports.put(fullName, new Spelling(spelling, importIt)); 234 } 235 return imports; 236 } 237 238 /** 239 * Handles the tricky case where the class being referred to is in {@code java.lang}, but the 240 * package of the referring code contains another class of the same name. For example, if the 241 * current package is {@code foo.bar} and there is a {@code foo.bar.Compiler}, then we will refer 242 * to {@code java.lang.Compiler} by its full name. The plain name {@code Compiler} would reference 243 * {@code foo.bar.Compiler} in this case. We need to write {@code java.lang.Compiler} even if the 244 * other {@code Compiler} class is not being considered here, so the {@link #ambiguousNames} logic 245 * is not enough. We have to look to see if the class exists. 246 */ javaLangSpelling( Elements elementUtils, String codePackageName, TypeElement typeElement)247 private static String javaLangSpelling( 248 Elements elementUtils, String codePackageName, TypeElement typeElement) { 249 // If this is java.lang.Thread.State or the like, we have to look for a clash with Thread. 250 TypeElement topLevelType = topLevelType(typeElement); 251 TypeElement clash = 252 elementUtils.getTypeElement(codePackageName + "." + topLevelType.getSimpleName()); 253 String fullName = typeElement.getQualifiedName().toString(); 254 return (clash == null) ? fullName.substring("java.lang.".length()) : fullName; 255 } 256 257 /** 258 * Finds the top-level types for all the declared types (classes and interfaces) in the given 259 * {@code Set<TypeMirror>}. 260 * 261 * <p>The returned set contains only top-level types. If we reference {@code java.util.Map.Entry} 262 * then the returned set will contain {@code java.util.Map}. This is because we want to write 263 * {@code Map.Entry} everywhere rather than {@code Entry}. 264 */ topLevelTypes(Types typeUtil, Set<TypeMirror> types)265 private static Set<TypeMirror> topLevelTypes(Types typeUtil, Set<TypeMirror> types) { 266 return types.stream() 267 .map(typeMirror -> MoreElements.asType(typeUtil.asElement(typeMirror))) 268 .map(typeElement -> topLevelType(typeElement).asType()) 269 .collect(toCollection(TypeMirrorSet::new)); 270 } 271 272 /** 273 * Finds all types that are declared with non private visibility by the given {@code TypeMirror}, 274 * any class in its superclass chain, or any interface it implements. 275 */ nonPrivateDeclaredTypes(Types typeUtils, TypeMirror type)276 private static Set<TypeMirror> nonPrivateDeclaredTypes(Types typeUtils, TypeMirror type) { 277 if (type == null) { 278 return new TypeMirrorSet(); 279 } else { 280 Set<TypeMirror> declared = new TypeMirrorSet(); 281 declared.add(type); 282 List<TypeElement> nestedTypes = 283 ElementFilter.typesIn(typeUtils.asElement(type).getEnclosedElements()); 284 for (TypeElement nestedType : nestedTypes) { 285 if (!nestedType.getModifiers().contains(PRIVATE)) { 286 declared.add(nestedType.asType()); 287 } 288 } 289 for (TypeMirror supertype : typeUtils.directSupertypes(type)) { 290 declared.addAll(nonPrivateDeclaredTypes(typeUtils, supertype)); 291 } 292 return declared; 293 } 294 } 295 ambiguousNames(Types typeUtils, Set<TypeMirror> types)296 private static Set<String> ambiguousNames(Types typeUtils, Set<TypeMirror> types) { 297 Set<String> ambiguous = new HashSet<>(); 298 Map<String, Name> simpleNamesToQualifiedNames = new HashMap<>(); 299 for (TypeMirror type : types) { 300 if (type.getKind() == TypeKind.ERROR) { 301 throw new MissingTypeException(MoreTypes.asError(type)); 302 } 303 String simpleName = typeUtils.asElement(type).getSimpleName().toString(); 304 /* 305 * Compare by qualified names, because in Eclipse JDT, if Java 8 type annotations are used, 306 * the same (unannotated) type may appear multiple times in the Set<TypeMirror>. 307 * TODO(emcmanus): investigate further, because this might cause problems elsewhere. 308 */ 309 Name qualifiedName = ((QualifiedNameable) typeUtils.asElement(type)).getQualifiedName(); 310 Name previous = simpleNamesToQualifiedNames.put(simpleName, qualifiedName); 311 if (previous != null && !previous.equals(qualifiedName)) { 312 ambiguous.add(simpleName); 313 } 314 } 315 return ambiguous; 316 } 317 318 /** 319 * Returns true if casting to the given type will elicit an unchecked warning from the compiler. 320 * Only generic types such as {@code List<String>} produce such warnings. There will be no warning 321 * if the type's only generic parameters are simple wildcards, as in {@code Map<?, ?>}. 322 */ isCastingUnchecked(TypeMirror type)323 static boolean isCastingUnchecked(TypeMirror type) { 324 return CASTING_UNCHECKED_VISITOR.visit(type, null); 325 } 326 327 private static final TypeVisitor<Boolean, Void> CASTING_UNCHECKED_VISITOR = 328 new SimpleTypeVisitor8<Boolean, Void>(false) { 329 @Override 330 public Boolean visitUnknown(TypeMirror t, Void p) { 331 // We don't know whether casting is unchecked for this mysterious type but assume it is, 332 // so we will insert a possibly unnecessary @SuppressWarnings("unchecked"). 333 return true; 334 } 335 336 @Override 337 public Boolean visitArray(ArrayType t, Void p) { 338 return visit(t.getComponentType(), p); 339 } 340 341 @Override 342 public Boolean visitDeclared(DeclaredType t, Void p) { 343 return t.getTypeArguments().stream().anyMatch(TypeSimplifier::uncheckedTypeArgument); 344 } 345 346 @Override 347 public Boolean visitTypeVariable(TypeVariable t, Void p) { 348 return true; 349 } 350 }; 351 352 // If a type has a type argument, then casting to the type is unchecked, except if the argument 353 // is <?> or <? extends Object>. The same applies to all type arguments, so casting to Map<?, ?> 354 // does not produce an unchecked warning for example. uncheckedTypeArgument(TypeMirror arg)355 private static boolean uncheckedTypeArgument(TypeMirror arg) { 356 if (arg.getKind() == TypeKind.WILDCARD) { 357 WildcardType wildcard = (WildcardType) arg; 358 if (wildcard.getExtendsBound() == null || isJavaLangObject(wildcard.getExtendsBound())) { 359 // This is <?>, unless there's a super bound, in which case it is <? super Foo> and 360 // is erased. 361 return (wildcard.getSuperBound() != null); 362 } 363 } 364 return true; 365 } 366 isJavaLangObject(TypeMirror type)367 private static boolean isJavaLangObject(TypeMirror type) { 368 if (type.getKind() != TypeKind.DECLARED) { 369 return false; 370 } 371 DeclaredType declaredType = (DeclaredType) type; 372 TypeElement typeElement = (TypeElement) declaredType.asElement(); 373 return typeElement.getQualifiedName().contentEquals("java.lang.Object"); 374 } 375 } 376