• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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