• 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
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