diff options
author | Liam Miller-Cushon <cushon@google.com> | 2017-10-15 23:31:56 -0700 |
---|---|---|
committer | Damien Martin-Guillerez <dmarting@google.com> | 2017-10-16 14:16:39 +0200 |
commit | 6bf3f268f4a01963a2ee13f60178664bb056a802 (patch) | |
tree | 32a870dc293e07af88f52b241c75d9cbd462f63f /third_party/checker_framework_javacutil | |
parent | 80a34dc97799961201e6dce20fd58dd08022c032 (diff) |
Update checker framework dataflow and javacutils to 2.1.14
Change-Id: I62ad827fc4bbd54d022097003af63e351e44b98c
Diffstat (limited to 'third_party/checker_framework_javacutil')
19 files changed, 2239 insertions, 1826 deletions
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AbstractTypeProcessor.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AbstractTypeProcessor.java index 04389039e8..114ce6d0b6 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AbstractTypeProcessor.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AbstractTypeProcessor.java @@ -1,136 +1,117 @@ package org.checkerframework.javacutil; -import java.util.HashSet; -import java.util.Set; - -import javax.annotation.processing.*; -import javax.lang.model.element.Name; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.ElementFilter; - -import com.sun.tools.javac.main.JavaCompiler; -import com.sun.tools.javac.comp.CompileStates.CompileState; -import com.sun.tools.javac.processing.JavacProcessingEnvironment; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.Log; - import com.sun.source.tree.ClassTree; import com.sun.source.util.JavacTask; import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; +import com.sun.tools.javac.comp.CompileStates.CompileState; +import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.processing.JavacProcessingEnvironment; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Log; +import java.util.HashSet; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.ElementFilter; /** - * This class is an abstract annotation processor designed to be a - * convenient superclass for concrete "type processors", processors that - * require the type information in the processed source. + * This class is an abstract annotation processor designed to be a convenient superclass for + * concrete "type processors", processors that require the type information in the processed source. * - * <p>Type processing occurs in one round after the tool (e.g. Java compiler) - * analyzes the source (all sources taken as input to the tool and sources - * generated by other annotation processors). + * <p>Type processing occurs in one round after the tool (e.g. Java compiler) analyzes the source + * (all sources taken as input to the tool and sources generated by other annotation processors). * - * <p>The tool infrastructure will interact with classes extending this abstract - * class as follows. + * <p>The tool infrastructure will interact with classes extending this abstract class as follows. * - * <p> - * 1-3 are Identical to the {@link Processor} life cycle. - * 4-5 are unique to {@code AbstractTypeProcessor} subclasses. + * <p>1-3 are identical to the {@link Processor} life cycle. 4-5 are unique to {@code + * AbstractTypeProcessor} subclasses. * * <ol> - * - * <li>If an existing {@code Processor} object is not being used, to - * create an instance of a processor the tool calls the no-arg - * constructor of the processor class. - * - * <li>Next, the tool calls the {@link #init init} method with - * an appropriate {@code ProcessingEnvironment}. - * - * <li>Afterwards, the tool calls {@link #getSupportedAnnotationTypes - * getSupportedAnnotationTypes}, {@link #getSupportedOptions - * getSupportedOptions}, and {@link #getSupportedSourceVersion - * getSupportedSourceVersion}. These methods are only called once per - * run, not on each round. - * - * - * <li>For each class containing a supported annotation, the tool calls - * {@link #typeProcess(TypeElement, TreePath) typeProcess} method on the - * {@code Processor}. The class is guaranteed to be type-checked Java code - * and all the tree type and symbol information is resolved. - * - * <li>Finally, the tools calls the - * {@link #typeProcessingOver() typeProcessingOver} method - * on the {@code Processor}. - * + * <li>If an existing {@code Processor} object is not being used, to create an instance of a + * processor the tool calls the no-arg constructor of the processor class. + * <li>Next, the tool calls the {@link #init init} method with an appropriate {@code + * ProcessingEnvironment}. + * <li>Afterwards, the tool calls {@link #getSupportedAnnotationTypes + * getSupportedAnnotationTypes}, {@link #getSupportedOptions getSupportedOptions}, and {@link + * #getSupportedSourceVersion getSupportedSourceVersion}. These methods are only called once + * per run, not on each round. + * <li>For each class containing a supported annotation, the tool calls {@link + * #typeProcess(TypeElement, TreePath) typeProcess} method on the {@code Processor}. The class + * is guaranteed to be type-checked Java code and all the tree type and symbol information is + * resolved. + * <li>Finally, the tools calls the {@link #typeProcessingOver() typeProcessingOver} method on the + * {@code Processor}. * </ol> * - * <p>The tool is permitted to ask type processors to process a class once - * it is analyzed before the rest of classes are analyzed. The tool is also - * permitted to stop type processing immediately if any errors are raised, - * without invoking {@code typeProcessingOver} + * <p>The tool is permitted to ask type processors to process a class once it is analyzed before the + * rest of classes are analyzed. The tool is also permitted to stop type processing immediately if + * any errors are raised, without invoking {@code typeProcessingOver} * - * <p>A subclass may override any of the methods in this class, as long as the - * general {@link javax.annotation.processing.Processor Processor} - * contract is obeyed, with one notable exception. - * {@link #process(Set, RoundEnvironment)} may not be overridden, as it - * is called during the declaration annotation phase before classes are analyzed. + * <p>A subclass may override any of the methods in this class, as long as the general {@link + * javax.annotation.processing.Processor Processor} contract is obeyed, with one notable exception. + * {@link #process(Set, RoundEnvironment)} may not be overridden, as it is called during the + * declaration annotation phase before classes are analyzed. * * @author Mahmood Ali * @author Werner Dietl */ public abstract class AbstractTypeProcessor extends AbstractProcessor { /** - * The set of fully-qualified element names that should be type-checked. - * We store the names of the elements, in order to prevent - * possible confusion between different Element instantiations. + * The set of fully-qualified element names that should be type-checked. We store the names of + * the elements, in order to prevent possible confusion between different Element + * instantiations. */ private final Set<Name> elements = new HashSet<Name>(); /** - * Method {@link #typeProcessingStart()} must be invoked exactly once, - * before any invocation of {@link #typeProcess(TypeElement, TreePath)}. + * Method {@link #typeProcessingStart()} must be invoked exactly once, before any invocation of + * {@link #typeProcess(TypeElement, TreePath)}. */ private boolean hasInvokedTypeProcessingStart = false; /** - * Method {@link #typeProcessingOver()} must be invoked exactly once, - * after the last invocation of {@link #typeProcess(TypeElement, TreePath)}. + * Method {@link #typeProcessingOver()} must be invoked exactly once, after the last invocation + * of {@link #typeProcess(TypeElement, TreePath)}. */ private static boolean hasInvokedTypeProcessingOver = false; - /** - * The TaskListener registered for completion of attribution. - */ + /** The TaskListener registered for completion of attribution. */ private final AttributionTaskListener listener = new AttributionTaskListener(); - /** - * Constructor for subclasses to call. - */ - protected AbstractTypeProcessor() { } + /** Constructor for subclasses to call. */ + protected AbstractTypeProcessor() {} /** * {@inheritDoc} * - * Register a TaskListener that will get called after FLOW. + * <p>Register a TaskListener that will get called after FLOW. */ @Override - public void init(ProcessingEnvironment env) { + public synchronized void init(ProcessingEnvironment env) { super.init(env); JavacTask.instance(env).addTaskListener(listener); Context ctx = ((JavacProcessingEnvironment) processingEnv).getContext(); JavaCompiler compiler = JavaCompiler.instance(ctx); - compiler.shouldStopPolicyIfNoError = CompileState.max(compiler.shouldStopPolicyIfNoError, - CompileState.FLOW); + compiler.shouldStopPolicyIfNoError = + CompileState.max(compiler.shouldStopPolicyIfNoError, CompileState.FLOW); + compiler.shouldStopPolicyIfError = + CompileState.max(compiler.shouldStopPolicyIfError, CompileState.FLOW); } /** - * The use of this method is obsolete in type processors. The method is - * called during declaration annotation processing phase only. - * It registers the names of elements to process. + * The use of this method is obsolete in type processors. The method is called during + * declaration annotation processing phase only. It registers the names of elements to process. */ @Override - public final boolean process(Set<? extends TypeElement> annotations, - RoundEnvironment roundEnv) { + public final boolean process( + Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (TypeElement elem : ElementFilter.typesIn(roundEnv.getRootElements())) { elements.add(elem.getQualifiedName()); } @@ -145,39 +126,35 @@ public abstract class AbstractTypeProcessor extends AbstractProcessor { public void typeProcessingStart() {} /** - * Processes a fully analyzed class that contains a supported annotation - * (look {@link #getSupportedAnnotationTypes()}). + * Processes a fully-analyzed class that contains a supported annotation (see {@link + * #getSupportedAnnotationTypes()}). * * <p>The passed class is always valid type-checked Java code. * - * @param element element of the analyzed class - * @param tree the tree path to the element, with the leaf being a - * {@link ClassTree} + * @param element element of the analyzed class + * @param tree the tree path to the element, with the leaf being a {@link ClassTree} */ public abstract void typeProcess(TypeElement element, TreePath tree); /** - * A method to be called once all the classes are processed and no error - * is reported. + * A method to be called once all the classes are processed and no error is reported. * - * <p>Subclasses may override this method to do any aggregate analysis - * (e.g. generate report, persistence) or resource deallocation. + * <p>Subclasses may override this method to do any aggregate analysis (e.g. generate report, + * persistence) or resource deallocation. * - * <p>If an error (a Java error or a processor error) is reported, this - * method is not guaranteed to be invoked. + * <p>If an error (a Java error or a processor error) is reported, this method is not guaranteed + * to be invoked. */ - public void typeProcessingOver() { } + public void typeProcessingOver() {} - /** - * A task listener that invokes the processor whenever a class is fully - * analyzed. - */ + /** A task listener that invokes the processor whenever a class is fully analyzed. */ private final class AttributionTaskListener implements TaskListener { @Override public void finished(TaskEvent e) { - if (e.getKind() != TaskEvent.Kind.ANALYZE) + if (e.getKind() != TaskEvent.Kind.ANALYZE) { return; + } if (!hasInvokedTypeProcessingStart) { typeProcessingStart(); @@ -191,13 +168,16 @@ public abstract class AbstractTypeProcessor extends AbstractProcessor { hasInvokedTypeProcessingOver = true; } - if (e.getTypeElement() == null) + if (e.getTypeElement() == null) { throw new AssertionError("event task without a type element"); - if (e.getCompilationUnit() == null) + } + if (e.getCompilationUnit() == null) { throw new AssertionError("event task without compilation unit"); + } - if (!elements.remove(e.getTypeElement().getQualifiedName())) + if (!elements.remove(e.getTypeElement().getQualifiedName())) { return; + } TypeElement elem = e.getTypeElement(); TreePath p = Trees.instance(processingEnv).getPath(elem); @@ -211,6 +191,6 @@ public abstract class AbstractTypeProcessor extends AbstractProcessor { } @Override - public void started(TaskEvent e) { } + public void started(TaskEvent e) {} } } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationProvider.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationProvider.java index 8ed857a9b2..1245960edd 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationProvider.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationProvider.java @@ -1,38 +1,28 @@ package org.checkerframework.javacutil; +import com.sun.source.tree.Tree; import java.lang.annotation.Annotation; - import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; -import com.sun.source.tree.Tree; - -/** - * An implementation of AnnotationProvider returns annotations on - * Java AST elements. - */ +/** An implementation of AnnotationProvider returns annotations on Java AST elements. */ public interface AnnotationProvider { /** - * Returns the actual annotation mirror used to annotate this type, - * whose name equals the passed annotationName if one exists, null otherwise. + * Returns the actual annotation mirror used to annotate this type, whose name equals the passed + * annotationName if one exists, null otherwise. * * @param anno annotation class * @return the annotation mirror for anno */ - public AnnotationMirror getDeclAnnotation(Element elt, - Class<? extends Annotation> anno); - + public AnnotationMirror getDeclAnnotation(Element elt, Class<? extends Annotation> anno); + /** - * Return the annotation on <code>tree</code> that has the class - * <code>target</code>. If no annotation for the given target class exists, - * the result is <code>null</code> - * - * @param tree - * The tree of which the annotation is returned - * @param target - * The class of the annotation + * Return the annotation on {@code tree} that has the class {@code target}. If no annotation for + * the given target class exists, the result is {@code null} + * + * @param tree the tree of which the annotation is returned + * @param target the class of the annotation */ - public AnnotationMirror getAnnotationMirror(Tree tree, - Class<? extends Annotation> target); + public AnnotationMirror getAnnotationMirror(Tree tree, Class<? extends Annotation> target); } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java index 54eb54d2fb..c80f57cc5d 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java @@ -7,7 +7,12 @@ import org.checkerframework.checker.nullness.qual.*; import org.checkerframework.checker.interning.qual.*; */ - +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.ModifiersTree; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.model.JavacElements; import java.lang.annotation.Annotation; import java.lang.annotation.Inherited; import java.util.ArrayList; @@ -21,7 +26,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; - import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ElementKind; @@ -33,23 +37,19 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; -import com.sun.tools.javac.code.Symbol.VarSymbol; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.model.JavacElements; -/** - * A utility class for working with annotations. - */ +/** A utility class for working with annotations. */ public class AnnotationUtils { // Class cannot be instantiated. - private AnnotationUtils() { throw new AssertionError("Class AnnotationUtils cannot be instantiated."); } + private AnnotationUtils() { + throw new AssertionError("Class AnnotationUtils cannot be instantiated."); + } // TODO: hack to clear out static state. - // {@link org.checkerframework.qualframework.util.QualifierContext} should - // handle instantiation of utility classes. public static void clear() { annotationsFromNames.clear(); annotationMirrorNames.clear(); + annotationMirrorSimpleNames.clear(); annotationClassNames.clear(); } @@ -58,68 +58,85 @@ public class AnnotationUtils { // ********************************************************************** /** Caching for annotation creation. */ - private static final Map<CharSequence, AnnotationMirror> annotationsFromNames - = new HashMap<CharSequence, AnnotationMirror>(); + private static final Map<CharSequence, AnnotationMirror> annotationsFromNames = + Collections.synchronizedMap(new HashMap<CharSequence, AnnotationMirror>()); + + private static final int ANNOTATION_CACHE_SIZE = 500; /** - * Cache names of AnnotationMirrors for faster access. Values in - * the map are interned Strings, so they can be compared with ==. + * Cache names of AnnotationMirrors for faster access. Values in the map are interned Strings, + * so they can be compared with ==. */ - private static final Map<AnnotationMirror, /*@Interned*/ String> annotationMirrorNames - = new HashMap<AnnotationMirror, /*@Interned*/ String>(); + private static final Map<AnnotationMirror, /*@Interned*/ String> annotationMirrorNames = + Collections.synchronizedMap( + CollectionUtils.<AnnotationMirror, /*@Interned*/ String>createLRUCache( + ANNOTATION_CACHE_SIZE)); /** - * Cache names of classes representing AnnotationMirrors for - * faster access. Values in the map are interned Strings, so they - * can be compared with ==. + * Cache simple names of AnnotationMirrors for faster access. Values in the map are interned + * Strings, so they can be compared with ==. */ - private static final Map<Class<? extends Annotation>, /*@Interned*/ String> annotationClassNames - = new HashMap<Class<? extends Annotation>, /*@Interned*/ String>(); + private static final Map<AnnotationMirror, /*@Interned*/ String> annotationMirrorSimpleNames = + Collections.synchronizedMap( + CollectionUtils.<AnnotationMirror, /*@Interned*/ String>createLRUCache( + ANNOTATION_CACHE_SIZE)); /** - * Creates an {@link AnnotationMirror} given by a particular - * fully-qualified name. getElementValues on the result returns an - * empty map. + * Cache names of classes representing AnnotationMirrors for faster access. Values in the map + * are interned Strings, so they can be compared with ==. + */ + private static final Map<Class<? extends Annotation>, /*@Interned*/ String> + annotationClassNames = + Collections.synchronizedMap( + new HashMap<Class<? extends Annotation>, /*@Interned*/ String>()); + + /** + * Creates an {@link AnnotationMirror} given by a particular fully-qualified name. + * getElementValues on the result returns an empty map. * * @param elements the element utilities to use * @param name the name of the annotation to create * @return an {@link AnnotationMirror} of type {@code} name */ public static AnnotationMirror fromName(Elements elements, CharSequence name) { - if (annotationsFromNames.containsKey(name)) - return annotationsFromNames.get(name); + AnnotationMirror res = annotationsFromNames.get(name); + if (res != null) { + return res; + } final DeclaredType annoType = typeFromName(elements, name); - if (annoType == null) + if (annoType == null) { return null; + } if (annoType.asElement().getKind() != ElementKind.ANNOTATION_TYPE) { ErrorReporter.errorAbort(annoType + " is not an annotation"); return null; // dead code } - AnnotationMirror result = new AnnotationMirror() { - String toString = "@" + annoType; - - @Override - public DeclaredType getAnnotationType() { - return annoType; - } - @Override - public Map<? extends ExecutableElement, ? extends AnnotationValue> - getElementValues() { - return Collections.emptyMap(); - } - /*@SideEffectFree*/ - @Override - public String toString() { - return toString; - } - }; + AnnotationMirror result = + new AnnotationMirror() { + String toString = "@" + annoType; + + @Override + public DeclaredType getAnnotationType() { + return annoType; + } + + @Override + public Map<? extends ExecutableElement, ? extends AnnotationValue> + getElementValues() { + return Collections.emptyMap(); + } + /*@SideEffectFree*/ + @Override + public String toString() { + return toString; + } + }; annotationsFromNames.put(name, result); return result; } /** - * Creates an {@link AnnotationMirror} given by a particular annotation - * class. + * Creates an {@link AnnotationMirror} given by a particular annotation class. * * @param elements the element utilities to use * @param clazz the annotation class @@ -130,8 +147,8 @@ public class AnnotationUtils { } /** - * A utility method that converts a {@link CharSequence} (usually a {@link - * String}) into a {@link TypeMirror} named thereby. + * A utility method that converts a {@link CharSequence} (usually a {@link String}) into a + * {@link TypeMirror} named thereby. * * @param elements the element utilities to use * @param name the name of a type @@ -139,26 +156,25 @@ public class AnnotationUtils { */ private static DeclaredType typeFromName(Elements elements, CharSequence name) { /*@Nullable*/ TypeElement typeElt = elements.getTypeElement(name); - if (typeElt == null) + if (typeElt == null) { return null; + } return (DeclaredType) typeElt.asType(); } - // ********************************************************************** // Helper methods to handle annotations. mainly workaround // AnnotationMirror.equals undesired property // (I think the undesired property is that it's reference equality.) // ********************************************************************** - /** - * @return the fully-qualified name of an annotation as a Name - */ + /** @return the fully-qualified name of an annotation as a String */ public static final /*@Interned*/ String annotationName(AnnotationMirror annotation) { - if (annotationMirrorNames.containsKey(annotation)) - return annotationMirrorNames.get(annotation); - + String res = annotationMirrorNames.get(annotation); + if (res != null) { + return res; + } final DeclaredType annoType = annotation.getAnnotationType(); final TypeElement elm = (TypeElement) annoType.asElement(); /*@Interned*/ String name = elm.getQualifiedName().toString().intern(); @@ -166,25 +182,40 @@ public class AnnotationUtils { return name; } + /** @return the simple name of an annotation as a String */ + public static String annotationSimpleName(AnnotationMirror annotation) { + String res = annotationMirrorSimpleNames.get(annotation); + if (res != null) { + return res; + } + final DeclaredType annoType = annotation.getAnnotationType(); + final TypeElement elm = (TypeElement) annoType.asElement(); + /*@Interned*/ String name = elm.getSimpleName().toString().intern(); + annotationMirrorSimpleNames.put(annotation, name); + return name; + } + /** * Checks if both annotations are the same. * - * Returns true iff both annotations are of the same type and have the - * same annotation values. This behavior differs from - * {@code AnnotationMirror.equals(Object)}. The equals method returns - * true iff both annotations are the same and annotate the same annotation - * target (e.g. field, variable, etc). + * <p>Returns true iff both annotations are of the same type and have the same annotation + * values. This behavior differs from {@code AnnotationMirror.equals(Object)}. The equals method + * returns true iff both annotations are the same and annotate the same annotation target (e.g. + * field, variable, etc). * * @return true iff a1 and a2 are the same annotation */ - public static boolean areSame(/*@Nullable*/ AnnotationMirror a1, /*@Nullable*/ AnnotationMirror a2) { + public static boolean areSame( + /*@Nullable*/ AnnotationMirror a1, /*@Nullable*/ AnnotationMirror a2) { if (a1 != null && a2 != null) { if (annotationName(a1) != annotationName(a2)) { return false; } - Map<? extends ExecutableElement, ? extends AnnotationValue> elval1 = getElementValuesWithDefaults(a1); - Map<? extends ExecutableElement, ? extends AnnotationValue> elval2 = getElementValuesWithDefaults(a2); + Map<? extends ExecutableElement, ? extends AnnotationValue> elval1 = + getElementValuesWithDefaults(a1); + Map<? extends ExecutableElement, ? extends AnnotationValue> elval2 = + getElementValuesWithDefaults(a2); return elval1.toString().equals(elval2.toString()); } @@ -198,30 +229,22 @@ public class AnnotationUtils { * @return true iff a1 and a2 have the same annotation type */ public static boolean areSameIgnoringValues(AnnotationMirror a1, AnnotationMirror a2) { - if (a1 != null && a2 != null) + if (a1 != null && a2 != null) { return annotationName(a1) == annotationName(a2); + } return a1 == a2; } - /** - * Checks that the annotation {@code am} has the name {@code aname}. Values - * are ignored. - */ + /** Checks that the annotation {@code am} has the name {@code aname}. Values are ignored. */ public static boolean areSameByName(AnnotationMirror am, /*@Interned*/ String aname) { // Both strings are interned. return annotationName(am) == aname; } - /** - * Checks that the annotation {@code am} has the name of {@code anno}. - * Values are ignored. - */ - public static boolean areSameByClass(AnnotationMirror am, - Class<? extends Annotation> anno) { - /*@Interned*/ String canonicalName; - if (annotationClassNames.containsKey(anno)) { - canonicalName = annotationClassNames.get(anno).intern(); - } else { + /** Checks that the annotation {@code am} has the name of {@code anno}. Values are ignored. */ + public static boolean areSameByClass(AnnotationMirror am, Class<? extends Annotation> anno) { + /*@Interned*/ String canonicalName = annotationClassNames.get(anno); + if (canonicalName == null) { canonicalName = anno.getCanonicalName().intern(); annotationClassNames.put(anno, canonicalName); } @@ -233,11 +256,14 @@ public class AnnotationUtils { * * @return true iff c1 and c2 contain the same annotations */ - public static boolean areSame(Collection<? extends AnnotationMirror> c1, Collection<? extends AnnotationMirror> c2) { - if (c1.size() != c2.size()) + public static boolean areSame( + Collection<? extends AnnotationMirror> c1, Collection<? extends AnnotationMirror> c2) { + if (c1.size() != c2.size()) { return false; - if (c1.size() == 1) + } + if (c1.size() == 1) { return areSame(c1.iterator().next(), c2.iterator().next()); + } Set<AnnotationMirror> s1 = createAnnotationSet(); Set<AnnotationMirror> s2 = createAnnotationSet(); @@ -251,78 +277,122 @@ public class AnnotationUtils { while (iter1.hasNext()) { AnnotationMirror anno1 = iter1.next(); AnnotationMirror anno2 = iter2.next(); - if (!areSame(anno1, anno2)) + if (!areSame(anno1, anno2)) { return false; + } } return true; } /** - * Checks that the collection contains the annotation. - * Using Collection.contains does not always work, because it - * does not use areSame for comparison. + * Checks that the collection contains the annotation. Using Collection.contains does not always + * work, because it does not use areSame for comparison. * - * @return true iff c contains anno, according to areSame. + * @return true iff c contains anno, according to areSame */ - public static boolean containsSame(Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { - for(AnnotationMirror an : c) { - if(AnnotationUtils.areSame(an, anno)) { - return true; - } - } - return false; + public static boolean containsSame( + Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { + return getSame(c, anno) != null; } /** - * Checks that the collection contains the annotation. - * Using Collection.contains does not always work, because it - * does not use areSame for comparison. + * Returns the AnnotationMirror in {@code c} that is the same annotation as {@code anno}. * - * @return true iff c contains anno, according to areSameByClass. + * @return AnnotationMirror with the same class as {@code anno} iff c contains anno, according + * to areSame; otherwise, {@code null} */ - public static boolean containsSameByClass(Collection<? extends AnnotationMirror> c, Class<? extends Annotation> anno) { - for(AnnotationMirror an : c) { - if(AnnotationUtils.areSameByClass(an, anno)) { - return true; + public static AnnotationMirror getSame( + Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { + for (AnnotationMirror an : c) { + if (AnnotationUtils.areSame(an, anno)) { + return an; } } - return false; + return null; } /** - * Checks that the collection contains the annotation ignoring values. - * Using Collection.contains does not always work, because it - * does not use areSameIgnoringValues for comparison. + * Checks that the collection contains the annotation. Using Collection.contains does not always + * work, because it does not use areSame for comparison. * - * @return true iff c contains anno, according to areSameIgnoringValues. + * @return true iff c contains anno, according to areSameByClass */ - public static boolean containsSameIgnoringValues(Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { - for(AnnotationMirror an : c) { - if(AnnotationUtils.areSameIgnoringValues(an, anno)) { - return true; + public static boolean containsSameByClass( + Collection<? extends AnnotationMirror> c, Class<? extends Annotation> anno) { + return getAnnotationByClass(c, anno) != null; + } + + /** + * Returns the AnnotationMirror in {@code c} that has the same class as {@code anno}. + * + * @return AnnotationMirror with the same class as {@code anno} iff c contains anno, according + * to areSameByClass; otherwise, {@code null} + */ + public static AnnotationMirror getAnnotationByClass( + Collection<? extends AnnotationMirror> c, Class<? extends Annotation> anno) { + for (AnnotationMirror an : c) { + if (AnnotationUtils.areSameByClass(an, anno)) { + return an; } } - return false; + return null; } - private static final Comparator<AnnotationMirror> ANNOTATION_ORDERING - = new Comparator<AnnotationMirror>() { - @Override - public int compare(AnnotationMirror a1, AnnotationMirror a2) { - String n1 = a1.toString(); - String n2 = a2.toString(); + /** + * Checks that the collection contains the annotation ignoring values. Using Collection.contains + * does not always work, because it does not use areSameIgnoringValues for comparison. + * + * @return true iff c contains anno, according to areSameIgnoringValues + */ + public static boolean containsSameIgnoringValues( + Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { + return getSameIgnoringValues(c, anno) != null; + } - return n1.compareTo(n2); + /** + * Returns the AnnotationMirror in {@code c} that is the same annotation as {@code anno} + * ignoring values. + * + * @return AnnotationMirror with the same class as {@code anno} iff c contains anno, according + * to areSameIgnoringValues; otherwise, {@code null} + */ + public static AnnotationMirror getSameIgnoringValues( + Collection<? extends AnnotationMirror> c, AnnotationMirror anno) { + for (AnnotationMirror an : c) { + if (AnnotationUtils.areSameIgnoringValues(an, anno)) { + return an; + } } - }; + return null; + } + + private static final Comparator<AnnotationMirror> ANNOTATION_ORDERING = + new Comparator<AnnotationMirror>() { + @Override + public int compare(AnnotationMirror a1, AnnotationMirror a2) { + // AnnotationMirror.toString() prints the elements of an annotation in the + // order in which they were written. So, use areSame to check for equality. + if (AnnotationUtils.areSame(a1, a2)) { + return 0; + } + + String n1 = a1.toString(); + String n2 = a2.toString(); + + // Because the AnnotationMirror.toString prints the annotation as it appears + // in source code, the order in which annotations of the same class are + // sorted may be confusing. For example, it might order + // @IntRange(from=1, to=MAX) before @IntRange(to=MAX,from=0). + return n1.compareTo(n2); + } + }; /** - * provide ordering for {@link AnnotationMirror} based on their fully - * qualified name. The ordering ignores annotation values when ordering. + * provide ordering for {@link AnnotationMirror} based on their fully qualified name. The + * ordering ignores annotation values when ordering. * - * The ordering is meant to be used as {@link TreeSet} or {@link TreeMap} - * ordering. A {@link Set} should not contain two annotations that only - * differ in values. + * <p>The ordering is meant to be used as {@link TreeSet} or {@link TreeMap} ordering. A {@link + * Set} should not contain two annotations that only differ in values. */ public static Comparator<AnnotationMirror> annotationOrdering() { return ANNOTATION_ORDERING; @@ -331,8 +401,8 @@ public class AnnotationUtils { /** * Create a map suitable for storing {@link AnnotationMirror} as keys. * - * It can store one instance of {@link AnnotationMirror} of a given - * declared type, regardless of the annotation element values. + * <p>It can store one instance of {@link AnnotationMirror} of a given declared type, regardless + * of the annotation element values. * * @param <V> the value of the map * @return a new map with {@link AnnotationMirror} as key @@ -344,8 +414,8 @@ public class AnnotationUtils { /** * Constructs a {@link Set} suitable for storing {@link AnnotationMirror}s. * - * It stores at most once instance of {@link AnnotationMirror} of a given - * type, regardless of the annotation element values. + * <p>It stores at most once instance of {@link AnnotationMirror} of a given type, regardless of + * the annotation element values. * * @return a new set to store {@link AnnotationMirror} as element */ @@ -358,62 +428,75 @@ public class AnnotationUtils { return anno.getAnnotationType().asElement().getAnnotation(Inherited.class) != null; } - // ********************************************************************** // Extractors for annotation values // ********************************************************************** /** - * Returns the values of an annotation's attributes, including defaults. - * The method with the same name in JavacElements cannot be used directly, - * because it includes a cast to Attribute.Compound, which doesn't hold - * for annotations generated by the Checker Framework. + * Returns the values of an annotation's attributes, including defaults. The method with the + * same name in JavacElements cannot be used directly, because it includes a cast to + * Attribute.Compound, which doesn't hold for annotations generated by the Checker Framework. * * @see AnnotationMirror#getElementValues() * @see JavacElements#getElementValuesWithDefaults(AnnotationMirror) - * - * @param ad annotation to examine + * @param ad annotation to examine * @return the values of the annotation's elements, including defaults */ public static Map<? extends ExecutableElement, ? extends AnnotationValue> - getElementValuesWithDefaults(AnnotationMirror ad) { - Map<ExecutableElement, AnnotationValue> valMap - = new HashMap<ExecutableElement, AnnotationValue>(); + getElementValuesWithDefaults(AnnotationMirror ad) { + Map<ExecutableElement, AnnotationValue> valMap = + new HashMap<ExecutableElement, AnnotationValue>(); if (ad.getElementValues() != null) { valMap.putAll(ad.getElementValues()); } for (ExecutableElement meth : - ElementFilter.methodsIn(ad.getAnnotationType().asElement().getEnclosedElements())) { + ElementFilter.methodsIn(ad.getAnnotationType().asElement().getEnclosedElements())) { AnnotationValue defaultValue = meth.getDefaultValue(); - if (defaultValue != null && !valMap.containsKey(meth)) + if (defaultValue != null && !valMap.containsKey(meth)) { valMap.put(meth, defaultValue); + } } return valMap; } /** - * Get the attribute with the name {@code name} of the annotation - * {@code anno}. The result is expected to have type {@code expectedType}. + * Verify whether the attribute with the name {@code name} exists in the annotation {@code + * anno}. * - * <p> - * <em>Note 1</em>: The method does not work well for attributes of an array - * type (as it would return a list of {@link AnnotationValue}s). Use - * {@code getElementValueArray} instead. + * @param anno the annotation to examine + * @param name the name of the attribute + * @return whether the attribute exists in anno + */ + public static <T> boolean hasElementValue(AnnotationMirror anno, CharSequence name) { + Map<? extends ExecutableElement, ? extends AnnotationValue> valmap = + anno.getElementValues(); + for (ExecutableElement elem : valmap.keySet()) { + if (elem.getSimpleName().contentEquals(name)) { + return true; + } + } + return false; + } + + /** + * Get the attribute with the name {@code name} of the annotation {@code anno}. The result is + * expected to have type {@code expectedType}. * - * <p> - * <em>Note 2</em>: The method does not work for attributes of an enum type, - * as the AnnotationValue is a VarSymbol and would be cast to the enum type, - * which doesn't work. Use {@code getElementValueEnum} instead. + * <p><em>Note 1</em>: The method does not work well for attributes of an array type (as it + * would return a list of {@link AnnotationValue}s). Use {@code getElementValueArray} instead. * + * <p><em>Note 2</em>: The method does not work for attributes of an enum type, as the + * AnnotationValue is a VarSymbol and would be cast to the enum type, which doesn't work. Use + * {@code getElementValueEnum} instead. * * @param anno the annotation to disassemble * @param name the name of the attribute to access * @param expectedType the expected type used to cast the return type - * @param useDefaults whether to apply default values to the attribute. + * @param useDefaults whether to apply default values to the attribute * @return the value of the attribute with the given name */ - public static <T> T getElementValue(AnnotationMirror anno, CharSequence name, - Class<T> expectedType, boolean useDefaults) { + public static <T> T getElementValue( + AnnotationMirror anno, CharSequence name, Class<T> expectedType, boolean useDefaults) { Map<? extends ExecutableElement, ? extends AnnotationValue> valmap; if (useDefaults) { valmap = getElementValuesWithDefaults(anno); @@ -430,34 +513,31 @@ public class AnnotationUtils { return null; // dead code } - /** - * Version that is suitable for Enum elements. - */ + /** Version that is suitable for Enum elements. */ public static <T extends Enum<T>> T getElementValueEnum( - AnnotationMirror anno, CharSequence name, Class<T> t, - boolean useDefaults) { + AnnotationMirror anno, CharSequence name, Class<T> t, boolean useDefaults) { VarSymbol vs = getElementValue(anno, name, VarSymbol.class, useDefaults); T value = Enum.valueOf(t, vs.getSimpleName().toString()); return value; } /** - * Get the attribute with the name {@code name} of the annotation - * {@code anno}, where the attribute has an array type. One element of the - * result is expected to have type {@code expectedType}. + * Get the attribute with the name {@code name} of the annotation {@code anno}, where the + * attribute has an array type. One element of the result is expected to have type {@code + * expectedType}. * - * Parameter useDefaults is used to determine whether default values - * should be used for annotation values. Finding defaults requires - * more computation, so should be false when no defaulting is needed. + * <p>Parameter useDefaults is used to determine whether default values should be used for + * annotation values. Finding defaults requires more computation, so should be false when no + * defaulting is needed. * * @param anno the annotation to disassemble * @param name the name of the attribute to access * @param expectedType the expected type used to cast the return type - * @param useDefaults whether to apply default values to the attribute. + * @param useDefaults whether to apply default values to the attribute * @return the value of the attribute with the given name */ - public static <T> List<T> getElementValueArray(AnnotationMirror anno, - CharSequence name, Class<T> expectedType, boolean useDefaults) { + public static <T> List<T> getElementValueArray( + AnnotationMirror anno, CharSequence name, Class<T> expectedType, boolean useDefaults) { @SuppressWarnings("unchecked") List<AnnotationValue> la = getElementValue(anno, name, List.class, useDefaults); List<T> result = new ArrayList<T>(la.size()); @@ -468,14 +548,13 @@ public class AnnotationUtils { } /** - * Get the attribute with the name {@code name} of the annotation - * {@code anno}, or the default value if no attribute is present explicitly, - * where the attribute has an array type and the elements are {@code Enum}s. - * One element of the result is expected to have type {@code expectedType}. + * Get the attribute with the name {@code name} of the annotation {@code anno}, or the default + * value if no attribute is present explicitly, where the attribute has an array type and the + * elements are {@code Enum}s. One element of the result is expected to have type {@code + * expectedType}. */ public static <T extends Enum<T>> List<T> getElementValueEnumArray( - AnnotationMirror anno, CharSequence name, Class<T> t, - boolean useDefaults) { + AnnotationMirror anno, CharSequence name, Class<T> t, boolean useDefaults) { @SuppressWarnings("unchecked") List<AnnotationValue> la = getElementValue(anno, name, List.class, useDefaults); List<T> result = new ArrayList<T>(la.size()); @@ -487,42 +566,58 @@ public class AnnotationUtils { } /** - * Get the Name of the class that is referenced by attribute 'name'. - * This is a convenience method for the most common use-case. - * Like getElementValue(anno, name, ClassType.class).getQualifiedName(), but - * this method ensures consistent use of the qualified name. + * Get the Name of the class that is referenced by attribute {@code name}. + * + * <p>This is a convenience method for the most common use-case. Like getElementValue(anno, + * name, ClassType.class).getQualifiedName(), but this method ensures consistent use of the + * qualified name. */ - public static Name getElementValueClassName(AnnotationMirror anno, CharSequence name, - boolean useDefaults) { + public static Name getElementValueClassName( + AnnotationMirror anno, CharSequence name, boolean useDefaults) { Type.ClassType ct = getElementValue(anno, name, Type.ClassType.class, useDefaults); // TODO: Is it a problem that this returns the type parameters too? Should I cut them off? return ct.asElement().getQualifiedName(); } + /** Get the list of Names of the classes that are referenced by attribute {@code name}. */ + public static List<Name> getElementValueClassNames( + AnnotationMirror anno, CharSequence name, boolean useDefaults) { + List<Type.ClassType> la = + getElementValueArray(anno, name, Type.ClassType.class, useDefaults); + List<Name> names = new ArrayList<>(); + for (Type.ClassType classType : la) { + names.add(classType.asElement().getQualifiedName()); + } + return names; + } + /** - * Get the Class that is referenced by attribute 'name'. - * This method uses Class.forName to load the class. It returns - * null if the class wasn't found. + * Get the Class that is referenced by attribute {@code name}. This method uses Class.forName to + * load the class. It returns null if the class wasn't found. */ - public static Class<?> getElementValueClass(AnnotationMirror anno, CharSequence name, - boolean useDefaults) { + public static Class<?> getElementValueClass( + AnnotationMirror anno, CharSequence name, boolean useDefaults) { Name cn = getElementValueClassName(anno, name, useDefaults); try { - Class<?> cls = Class.forName(cn.toString()); + ClassLoader classLoader = InternalUtils.getClassLoaderForClass(AnnotationUtils.class); + Class<?> cls = Class.forName(cn.toString(), true, classLoader); return cls; } catch (ClassNotFoundException e) { - ErrorReporter.errorAbort("Could not load class '" + cn + "' for field '" + name + - "' in annotation " + anno, e); + String msg = + String.format( + "Could not load class '%s' for field '%s' in annotation %s", + cn, name, anno); + ErrorReporter.errorAbort(msg, e); return null; // dead code } } /** - * See checkers.types.QualifierHierarchy#updateMappingToMutableSet(QualifierHierarchy, Map, Object, AnnotationMirror) - * (Not linked because it is in an independent project. + * See checkers.types.QualifierHierarchy#updateMappingToMutableSet(QualifierHierarchy, Map, + * Object, AnnotationMirror) (Not linked because it is in an independent project. */ - public static <T> void updateMappingToImmutableSet(Map<T, Set<AnnotationMirror>> map, - T key, Set<AnnotationMirror> newQual) { + public static <T> void updateMappingToImmutableSet( + Map<T, Set<AnnotationMirror>> map, T key, Set<AnnotationMirror> newQual) { Set<AnnotationMirror> result = AnnotationUtils.createAnnotationSet(); // TODO: if T is also an AnnotationMirror, should we use areSame? @@ -534,4 +629,22 @@ public class AnnotationUtils { } map.put(key, Collections.unmodifiableSet(result)); } + + /** + * Returns the annotations explicitly written on a constructor result. Callers should check that + * {@code constructorDeclaration} is in fact a declaration of a constructor. + * + * @param constructorDeclaration declaration tree of constructor + * @return set of annotations explicit on the resulting type of the constructor + */ + public static Set<AnnotationMirror> getExplicitAnnotationsOnConstructorResult( + MethodTree constructorDeclaration) { + Set<AnnotationMirror> annotationSet = AnnotationUtils.createAnnotationSet(); + ModifiersTree modifiersTree = constructorDeclaration.getModifiers(); + if (modifiersTree != null) { + List<? extends AnnotationTree> annotationTrees = modifiersTree.getAnnotations(); + annotationSet.addAll(InternalUtils.annotationsFromTypeAnnotationTrees(annotationTrees)); + } + return annotationSet; + } } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicAnnotationProvider.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicAnnotationProvider.java index b885b5cbea..6cf40e79cf 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicAnnotationProvider.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicAnnotationProvider.java @@ -1,21 +1,16 @@ package org.checkerframework.javacutil; +import com.sun.source.tree.Tree; import java.lang.annotation.Annotation; - import java.util.List; - import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; -import com.sun.source.tree.Tree; - public class BasicAnnotationProvider implements AnnotationProvider { @Override - public AnnotationMirror getDeclAnnotation(Element elt, - Class<? extends Annotation> anno) { - List<? extends AnnotationMirror> annotationMirrors = elt - .getAnnotationMirrors(); + public AnnotationMirror getDeclAnnotation(Element elt, Class<? extends Annotation> anno) { + List<? extends AnnotationMirror> annotationMirrors = elt.getAnnotationMirrors(); // Then look at the real annotations. for (AnnotationMirror am : annotationMirrors) { @@ -28,8 +23,7 @@ public class BasicAnnotationProvider implements AnnotationProvider { } @Override - public AnnotationMirror getAnnotationMirror(Tree tree, - Class<? extends Annotation> target) { + public AnnotationMirror getAnnotationMirror(Tree tree, Class<? extends Annotation> target) { return null; } } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicTypeProcessor.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicTypeProcessor.java index c83a00021c..6b68fa4dc2 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicTypeProcessor.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicTypeProcessor.java @@ -1,28 +1,22 @@ package org.checkerframework.javacutil; -import javax.lang.model.element.TypeElement; - import com.sun.source.tree.CompilationUnitTree; - import com.sun.source.util.TreePath; import com.sun.source.util.TreePathScanner; +import javax.lang.model.element.TypeElement; /** - * Process the types in an AST in a trivial manner, with hooks for derived classes - * to actually do something. + * Process the types in an AST in a trivial manner, with hooks for derived classes to actually do + * something. */ public abstract class BasicTypeProcessor extends AbstractTypeProcessor { /** The source tree that's being scanned. */ protected CompilationUnitTree currentRoot; - /** - * Create a TreePathScanner at the given root. - */ + /** Create a TreePathScanner at the given root. */ protected abstract TreePathScanner<?, ?> createTreePathScanner(CompilationUnitTree root); - /** - * Visit the tree path for the type element. - */ + /** Visit the tree path for the type element. */ @Override public void typeProcess(TypeElement e, TreePath p) { currentRoot = p.getCompilationUnit(); @@ -32,12 +26,12 @@ public abstract class BasicTypeProcessor extends AbstractTypeProcessor { scanner = createTreePathScanner(currentRoot); scanner.scan(p, null); } catch (Throwable t) { - System.err.println("BasicTypeProcessor.typeProcess: unexpected Throwable (" + - t.getClass().getSimpleName() + ") when processing " - + currentRoot.getSourceFile().getName() + - (t.getMessage()!=null ? "; message: " + t.getMessage() : "")); + System.err.println( + "BasicTypeProcessor.typeProcess: unexpected Throwable (" + + t.getClass().getSimpleName() + + ") when processing " + + currentRoot.getSourceFile().getName() + + (t.getMessage() != null ? "; message: " + t.getMessage() : "")); } } - } - diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/CollectionUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/CollectionUtils.java new file mode 100644 index 0000000000..a6a58250fc --- /dev/null +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/CollectionUtils.java @@ -0,0 +1,26 @@ +package org.checkerframework.javacutil; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** Utility methods related to Java Collections */ +public class CollectionUtils { + + /** + * A Utility method for creating LRU cache + * + * @param size size of the cache + * @return a new cache with the provided size + */ + public static <K, V> Map<K, V> createLRUCache(final int size) { + return new LinkedHashMap<K, V>() { + + private static final long serialVersionUID = 5261489276168775084L; + + @Override + protected boolean removeEldestEntry(Map.Entry<K, V> entry) { + return size() > size; + } + }; + } +} diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ElementUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ElementUtils.java index 160a9c2eea..4abfa86cb7 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ElementUtils.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ElementUtils.java @@ -8,6 +8,7 @@ import static com.sun.tools.javac.code.Flags.ABSTRACT; import static com.sun.tools.javac.code.Flags.EFFECTIVELY_FINAL; import static com.sun.tools.javac.code.Flags.FINAL; +import com.sun.tools.javac.code.Symbol; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; @@ -16,7 +17,7 @@ import java.util.Deque; import java.util.HashSet; import java.util.List; import java.util.Set; - +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -31,26 +32,23 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; -import com.sun.tools.javac.code.Symbol; - -/** - * A Utility class for analyzing {@code Element}s. - */ +/** A Utility class for analyzing {@code Element}s. */ public class ElementUtils { // Class cannot be instantiated. - private ElementUtils() { throw new AssertionError("Class ElementUtils cannot be instantiated."); } + private ElementUtils() { + throw new AssertionError("Class ElementUtils cannot be instantiated."); + } /** * Returns the innermost type element enclosing the given element * * @param elem the enclosed element of a class - * @return the innermost type element + * @return the innermost type element */ public static TypeElement enclosingClass(final Element elem) { Element result = elem; - while (result != null && !result.getKind().isClass() - && !result.getKind().isInterface()) { + while (result != null && !result.getKind().isClass() && !result.getKind().isInterface()) { /*@Nullable*/ Element encl = result.getEnclosingElement(); result = encl; } @@ -58,13 +56,12 @@ public class ElementUtils { } /** - * Returns the innermost package element enclosing the given element. - * The same effect as {@link javax.lang.model.util.Elements#getPackageOf(Element)}. - * Returns the element itself if it is a package. + * Returns the innermost package element enclosing the given element. The same effect as {@link + * javax.lang.model.util.Elements#getPackageOf(Element)}. Returns the element itself if it is a + * package. * * @param elem the enclosed element of a package * @return the innermost package element - * */ public static PackageElement enclosingPackage(final Element elem) { Element result = elem; @@ -76,20 +73,21 @@ public class ElementUtils { } /** - * Returns the "parent" package element for the given package element. - * For package "A.B" it gives "A". - * For package "A" it gives the default package. - * For the default package it returns null; + * Returns the "parent" package element for the given package element. For package "A.B" it + * gives "A". For package "A" it gives the default package. For the default package it returns + * null; * - * Note that packages are not enclosed within each other, we have to manually climb - * the namespaces. Calling "enclosingPackage" on a package element returns the - * package element itself again. + * <p>Note that packages are not enclosed within each other, we have to manually climb the + * namespaces. Calling "enclosingPackage" on a package element returns the package element + * itself again. * * @param elem the package to start from * @return the parent package element - * */ public static PackageElement parentPackage(final Elements e, final PackageElement elem) { + // The following might do the same thing: + // ((Symbol) elt).owner; + // TODO: verify and see whether the change is worth it. String fqnstart = elem.getQualifiedName().toString(); String fqn = fqnstart; if (fqn != null && !fqn.isEmpty() && fqn.contains(".")) { @@ -100,10 +98,9 @@ public class ElementUtils { } /** - * Returns true if the element is a static element: whether it is a static - * field, static method, or static class + * Returns true if the element is a static element: whether it is a static field, static method, + * or static class * - * @param element * @return true if element is static */ public static boolean isStatic(Element element) { @@ -111,10 +108,8 @@ public class ElementUtils { } /** - * Returns true if the element is a final element: a final field, final - * method, or final class + * Returns true if the element is a final element: a final field, final method, or final class * - * @param element * @return true if the element is final */ public static boolean isFinal(Element element) { @@ -124,44 +119,39 @@ public class ElementUtils { /** * Returns true if the element is a effectively final element. * - * @param element * @return true if the element is effectively final */ public static boolean isEffectivelyFinal(Element element) { Symbol sym = (Symbol) element; - if (sym.getEnclosingElement().getKind() == ElementKind.METHOD && - (sym.getEnclosingElement().flags() & ABSTRACT) != 0) { + if (sym.getEnclosingElement().getKind() == ElementKind.METHOD + && (sym.getEnclosingElement().flags() & ABSTRACT) != 0) { return true; } return (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0; } /** - * Returns the {@code TypeMirror} for usage of Element as a value. It - * returns the return type of a method element, the class type of a - * constructor, or simply the type mirror of the element itself. + * Returns the {@code TypeMirror} for usage of Element as a value. It returns the return type of + * a method element, the class type of a constructor, or simply the type mirror of the element + * itself. * - * @param element - * @return the type for the element used as a value + * @return the type for the element used as a value */ public static TypeMirror getType(Element element) { - if (element.getKind() == ElementKind.METHOD) - return ((ExecutableElement)element).getReturnType(); - else if (element.getKind() == ElementKind.CONSTRUCTOR) + if (element.getKind() == ElementKind.METHOD) { + return ((ExecutableElement) element).getReturnType(); + } else if (element.getKind() == ElementKind.CONSTRUCTOR) { return enclosingClass(element).asType(); - else + } else { return element.asType(); + } } /** - * Returns the qualified name of the inner most class enclosing - * the provided {@code Element} + * Returns the qualified name of the inner most class enclosing the provided {@code Element} * - * @param element - * an element enclosed by a class, or a - * {@code TypeElement} - * @return The qualified {@code Name} of the innermost class - * enclosing the element + * @param element an element enclosed by a class, or a {@code TypeElement} + * @return the qualified {@code Name} of the innermost class enclosing the element */ public static /*@Nullable*/ Name getQualifiedClassName(Element element) { if (element.getKind() == ElementKind.PACKAGE) { @@ -170,18 +160,18 @@ public class ElementUtils { } TypeElement elem = enclosingClass(element); - if (elem == null) + if (elem == null) { return null; + } return elem.getQualifiedName(); } - /** - * Returns a verbose name that identifies the element. - */ + /** Returns a verbose name that identifies the element. */ public static String getVerboseName(Element elt) { - if (elt.getKind() == ElementKind.PACKAGE || - elt.getKind().isClass()) { + if (elt.getKind() == ElementKind.PACKAGE + || elt.getKind().isClass() + || elt.getKind().isInterface()) { return getQualifiedClassName(elt).toString(); } else { return getQualifiedClassName(elt) + "." + elt.toString(); @@ -191,34 +181,33 @@ public class ElementUtils { /** * Check if the element is an element for 'java.lang.Object' * - * @param element the type element + * @param element the type element * @return true iff the element is java.lang.Object element */ public static boolean isObject(TypeElement element) { return element.getQualifiedName().contentEquals("java.lang.Object"); } - /** - * Returns true if the element is a constant time reference - */ + /** Returns true if the element is a constant time reference */ public static boolean isCompileTimeConstant(Element elt) { return elt != null - && elt.getKind() == ElementKind.FIELD - && ((VariableElement)elt).getConstantValue() != null; + && (elt.getKind() == ElementKind.FIELD + || elt.getKind() == ElementKind.LOCAL_VARIABLE) + && ((VariableElement) elt).getConstantValue() != null; } /** - * Returns true if the element is declared in ByteCode. - * Always return false if elt is a package. + * Returns true if the element is declared in ByteCode. Always return false if elt is a package. */ - public static boolean isElementFromByteCode(Element elt){ - if (elt == null) + public static boolean isElementFromByteCode(Element elt) { + if (elt == null) { return false; + } - if (elt instanceof Symbol.ClassSymbol){ + if (elt instanceof Symbol.ClassSymbol) { Symbol.ClassSymbol clss = (Symbol.ClassSymbol) elt; - if (null != clss.classfile){ - //The class file could be a .java file + if (null != clss.classfile) { + // The class file could be a .java file return clss.classfile.getName().endsWith(".class"); } else { return false; @@ -228,19 +217,19 @@ public class ElementUtils { } /** - * Returns true if the element is declared in ByteCode. - * Always return false if elt is a package. + * Returns true if the element is declared in ByteCode. Always return false if elt is a package. */ - private static boolean isElementFromByteCode(Element elt, Element orig){ - if (elt == null) + private static boolean isElementFromByteCode(Element elt, Element orig) { + if (elt == null) { return false; - if (elt instanceof Symbol.ClassSymbol){ + } + if (elt instanceof Symbol.ClassSymbol) { Symbol.ClassSymbol clss = (Symbol.ClassSymbol) elt; - if (null != clss.classfile){ + if (null != clss.classfile) { // The class file could be a .java file - return (clss.classfile.getName().endsWith(".class") || - clss.classfile.getName().endsWith(".class)") || - clss.classfile.getName().endsWith(".class)]")); + return (clss.classfile.getName().endsWith(".class") + || clss.classfile.getName().endsWith(".class)") + || clss.classfile.getName().endsWith(".class)]")); } else { return false; } @@ -248,11 +237,9 @@ public class ElementUtils { return isElementFromByteCode(elt.getEnclosingElement(), elt); } - /** - * Returns the field of the class - */ + /** Returns the field of the class */ public static VariableElement findFieldInType(TypeElement type, String name) { - for (VariableElement field: ElementFilter.fieldsIn(type.getEnclosedElements())) { + for (VariableElement field : ElementFilter.fieldsIn(type.getEnclosedElements())) { if (field.getSimpleName().toString().equals(name)) { return field; } @@ -260,9 +247,22 @@ public class ElementUtils { return null; } - public static Set<VariableElement> findFieldsInType(TypeElement type, Collection<String> names) { + /** + * Returns the elements of the fields whose simple names are {@code names} and are declared in + * {@code type}. + * + * <p>If a field isn't declared in {@code type}, its element isn't included in the returned set. + * If none of the fields is declared in {@code type}, the empty set is returned. + * + * @param type where to look for fields + * @param names simple names of fields that might be declared in {@code type} + * @return the elements of the fields whose simple names are {@code names} and are declared in + * {@code type} + */ + public static Set<VariableElement> findFieldsInType( + TypeElement type, Collection<String> names) { Set<VariableElement> results = new HashSet<VariableElement>(); - for (VariableElement field: ElementFilter.fieldsIn(type.getEnclosedElements())) { + for (VariableElement field : ElementFilter.fieldsIn(type.getEnclosedElements())) { if (names.contains(field.getSimpleName().toString())) { results.add(field); } @@ -270,35 +270,87 @@ public class ElementUtils { return results; } + /** + * Returns non-private field elements, and side-effects {@code names} to remove them. For every + * field name in {@code names} that is declared in {@code type} or a supertype, add its element + * to the returned set and remove it from {@code names}. + * + * <p>When this routine returns, the combination of the return value and {@code names} has the + * same cardinality, and represents the same fields, as {@code names} did when the method was + * called. + * + * @param type where to look for fields + * @param names simple names of fields that might be declared in {@code type} or a supertype. + * (Names that are found are removed from this list.) + * @return the {@code VariableElement}s for non-private fields that are declared in {@code type} + * whose simple names were in {@code names} when the method was called. + */ + public static Set<VariableElement> findFieldsInTypeOrSuperType( + TypeMirror type, Collection<String> names) { + Set<VariableElement> elements = new HashSet<>(); + findFieldsInTypeOrSuperType(type, names, elements); + return elements; + } + + /** + * Side-effects both {@code foundFields} (which starts empty) and {@code notFound}, conceptually + * moving elements from {@code notFound} to {@code foundFields}. + */ + private static void findFieldsInTypeOrSuperType( + TypeMirror type, Collection<String> notFound, Set<VariableElement> foundFields) { + if (TypesUtils.isObject(type)) { + return; + } + TypeElement elt = InternalUtils.getTypeElement(type); + + Set<VariableElement> fieldElts = findFieldsInType(elt, notFound); + for (VariableElement field : new HashSet<>(fieldElts)) { + if (!field.getModifiers().contains(Modifier.PRIVATE)) { + notFound.remove(field.getSimpleName().toString()); + } else { + fieldElts.remove(field); + } + } + foundFields.addAll(fieldElts); + + if (!notFound.isEmpty()) { + findFieldsInTypeOrSuperType(elt.getSuperclass(), notFound, foundFields); + } + } + public static boolean isError(Element element) { - return element.getClass().getName().equals("com.sun.tools.javac.comp.Resolve$SymbolNotFoundError"); + return element.getClass() + .getName() + .equals("com.sun.tools.javac.comp.Resolve$SymbolNotFoundError"); } /** - * Does the given element need a receiver for accesses? - * For example, an access to a local variable does not require a receiver. + * Does the given element need a receiver for accesses? For example, an access to a local + * variable does not require a receiver. * - * @param element The element to test. - * @return whether the element requires a receiver for accesses. + * @param element the element to test + * @return whether the element requires a receiver for accesses */ public static boolean hasReceiver(Element element) { - return element.getKind() != ElementKind.LOCAL_VARIABLE - && element.getKind() != ElementKind.PARAMETER - && element.getKind() != ElementKind.PACKAGE + return (element.getKind().isField() + || element.getKind() == ElementKind.METHOD + || element.getKind() == ElementKind.CONSTRUCTOR) && !ElementUtils.isStatic(element); } /** - * Determine all type elements for the classes and interfaces referenced - * in the extends/implements clauses of the given type element. - * TODO: can we learn from the implementation of + * Determine all type elements for the classes and interfaces referenced (directly or + * indirectly) in the extends/implements clauses of the given type element. + * + * <p>TODO: can we learn from the implementation of * com.sun.tools.javac.model.JavacElements.getAllMembers(TypeElement)? */ - public static List<TypeElement> getSuperTypes(TypeElement type) { + public static List<TypeElement> getSuperTypes(Elements elements, TypeElement type) { List<TypeElement> superelems = new ArrayList<TypeElement>(); - if (type == null) + if (type == null) { return superelems; + } // Set up a stack containing type, which is our starting point. Deque<TypeElement> stack = new ArrayDeque<TypeElement>(); @@ -310,16 +362,27 @@ public class ElementUtils { // For each direct supertype of the current type element, if it // hasn't already been visited, push it onto the stack and // add it to our superelems set. - TypeMirror supertypecls = current.getSuperclass(); - if (supertypecls.getKind() != TypeKind.NONE) { - TypeElement supercls = (TypeElement) ((DeclaredType)supertypecls).asElement(); + TypeMirror supertypecls; + try { + supertypecls = current.getSuperclass(); + } catch (com.sun.tools.javac.code.Symbol.CompletionFailure cf) { + // Looking up a supertype failed. This sometimes happens + // when transitive dependencies are not on the classpath. + // As javac didn't complain, let's also not complain. + // TODO: Use an expanded ErrorReporter to output a message. + supertypecls = null; + } + + if (supertypecls != null && supertypecls.getKind() != TypeKind.NONE) { + TypeElement supercls = (TypeElement) ((DeclaredType) supertypecls).asElement(); if (!superelems.contains(supercls)) { stack.push(supercls); superelems.add(supercls); } } + for (TypeMirror supertypeitf : current.getInterfaces()) { - TypeElement superitf = (TypeElement) ((DeclaredType)supertypeitf).asElement(); + TypeElement superitf = (TypeElement) ((DeclaredType) supertypeitf).asElement(); if (!superelems.contains(superitf)) { stack.push(superitf); superelems.add(superitf); @@ -327,18 +390,24 @@ public class ElementUtils { } } + // Include java.lang.Object as implicit superclass for all classes and interfaces. + TypeElement jlobject = elements.getTypeElement("java.lang.Object"); + if (!superelems.contains(jlobject)) { + superelems.add(jlobject); + } + return Collections.<TypeElement>unmodifiableList(superelems); } /** - * Return all fields declared in the given type or any superclass/interface. - * TODO: should this use javax.lang.model.util.Elements.getAllMembers(TypeElement) - * instead of our own getSuperTypes? + * Return all fields declared in the given type or any superclass/interface. TODO: should this + * use javax.lang.model.util.Elements.getAllMembers(TypeElement) instead of our own + * getSuperTypes? */ - public static List<VariableElement> getAllFieldsIn(TypeElement type) { + public static List<VariableElement> getAllFieldsIn(Elements elements, TypeElement type) { List<VariableElement> fields = new ArrayList<VariableElement>(); fields.addAll(ElementFilter.fieldsIn(type.getEnclosedElements())); - List<TypeElement> alltypes = getSuperTypes(type); + List<TypeElement> alltypes = getSuperTypes(elements, type); for (TypeElement atype : alltypes) { fields.addAll(ElementFilter.fieldsIn(atype.getEnclosedElements())); } @@ -346,16 +415,15 @@ public class ElementUtils { } /** - * Return all methods declared in the given type or any superclass/interface. - * Note that no constructors will be returned. - * TODO: should this use javax.lang.model.util.Elements.getAllMembers(TypeElement) - * instead of our own getSuperTypes? + * Return all methods declared in the given type or any superclass/interface. Note that no + * constructors will be returned. TODO: should this use + * javax.lang.model.util.Elements.getAllMembers(TypeElement) instead of our own getSuperTypes? */ - public static List<ExecutableElement> getAllMethodsIn(TypeElement type) { + public static List<ExecutableElement> getAllMethodsIn(Elements elements, TypeElement type) { List<ExecutableElement> meths = new ArrayList<ExecutableElement>(); meths.addAll(ElementFilter.methodsIn(type.getEnclosedElements())); - List<TypeElement> alltypes = getSuperTypes(type); + List<TypeElement> alltypes = getSuperTypes(elements, type); for (TypeElement atype : alltypes) { meths.addAll(ElementFilter.methodsIn(atype.getEnclosedElements())); } @@ -364,8 +432,8 @@ public class ElementUtils { public static boolean isTypeDeclaration(Element elt) { switch (elt.getKind()) { - // These tree kinds are always declarations. Uses of the declared - // types have tree kind IDENTIFIER. + // These tree kinds are always declarations. Uses of the declared + // types have tree kind IDENTIFIER. case ANNOTATION_TYPE: case CLASS: case ENUM: @@ -381,17 +449,16 @@ public class ElementUtils { /** * Check that a method Element matches a signature. * - * Note: Matching the receiver type must be done elsewhere as - * the Element receiver type is only populated when annotated. + * <p>Note: Matching the receiver type must be done elsewhere as the Element receiver type is + * only populated when annotated. * * @param method the method Element * @param methodName the name of the method * @param parameters the formal parameters' Classes * @return true if the method matches */ - public static boolean matchesElement(ExecutableElement method, - String methodName, - Class<?> ... parameters) { + public static boolean matchesElement( + ExecutableElement method, String methodName, Class<?>... parameters) { if (!method.getSimpleName().toString().equals(methodName)) { return false; @@ -401,8 +468,11 @@ public class ElementUtils { return false; } else { for (int i = 0; i < method.getParameters().size(); i++) { - if (!method.getParameters().get(i).asType().toString().equals( - parameters[i].getName())) { + if (!method.getParameters() + .get(i) + .asType() + .toString() + .equals(parameters[i].getName())) { return false; } @@ -411,4 +481,12 @@ public class ElementUtils { return true; } + + /** Returns true if the given element is, or overrides, method. */ + public static boolean isMethod( + ExecutableElement questioned, ExecutableElement method, ProcessingEnvironment env) { + TypeElement enclosing = (TypeElement) questioned.getEnclosingElement(); + return questioned.equals(method) + || env.getElementUtils().overrides(questioned, method, enclosing); + } } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorHandler.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorHandler.java index 3fd0faa475..76ee74eaca 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorHandler.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorHandler.java @@ -1,16 +1,15 @@ package org.checkerframework.javacutil; /** - * An implementation of the ErrorHandler interface can be registered - * with the ErrorReporter class to change the default behavior on - * errors. + * An implementation of the ErrorHandler interface can be registered with the ErrorReporter class to + * change the default behavior on errors. */ public interface ErrorHandler { /** * Log an error message and abort processing. * - * @param msg The error message to log. + * @param msg the error message to log */ public void errorAbort(String msg); diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorReporter.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorReporter.java index 072e1a1031..4ff62e05d1 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorReporter.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorReporter.java @@ -1,27 +1,24 @@ package org.checkerframework.javacutil; /** - * Handle errors detected in utility classes. By default, the error reporter - * throws a RuntimeException, but clients of the utility library may register - * a handler to change the behavior. For example, type checkers can direct - * errors to the org.checkerframework.framework.source.SourceChecker class. + * Handle errors detected in utility classes. By default, the error reporter throws a + * RuntimeException, but clients of the utility library may register a handler to change the + * behavior. For example, type checkers can direct errors to the + * org.checkerframework.framework.source.SourceChecker class. */ public class ErrorReporter { protected static ErrorHandler handler = null; - /** - * Register a handler to customize error reporting. - */ + /** Register a handler to customize error reporting. */ public static void setHandler(ErrorHandler h) { handler = h; } /** - * Log an error message and abort processing. - * Call this method instead of raising an exception. + * Log an error message and abort processing. Call this method instead of raising an exception. * - * @param msg The error message to log. + * @param msg the error message to log */ public static void errorAbort(String msg) { if (handler != null) { @@ -31,6 +28,22 @@ public class ErrorReporter { } } + /** + * Log an error message use {@link String#format(String, Object...)}} and abort processing. Call + * this method instead of raising an exception. + * + * @param format a format string + * @param args arguments to the format string + */ + public static void errorAbort(String format, Object... args) { + String formattedMsg = String.format(format, args); + if (handler != null) { + handler.errorAbort(formattedMsg); + } else { + throw new RuntimeException(formattedMsg, new Throwable()); + } + } + public static void errorAbort(String msg, Throwable cause) { if (handler != null) { handler.errorAbort(msg, cause); diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/InternalUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/InternalUtils.java index 40cb4747f5..10a48cd719 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/InternalUtils.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/InternalUtils.java @@ -1,19 +1,5 @@ package org.checkerframework.javacutil; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.type.TypeVariable; -import javax.lang.model.type.WildcardType; -import javax.lang.model.util.Elements; - import com.sun.source.tree.AnnotatedTypeTree; import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.ArrayAccessTree; @@ -29,6 +15,7 @@ import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Type.CapturedType; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.sun.tools.javac.tree.JCTree; @@ -43,15 +30,29 @@ import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; +import javax.lang.model.util.Elements; /*>>> - import org.checkerframework.checker.nullness.qual.*; - */ +import org.checkerframework.checker.nullness.qual.*; +*/ /** - * Static utility methods used by annotation abstractions in this package. Some - * methods in this class depend on the use of Sun javac internals; any procedure - * in the Checker Framework that uses a non-public API should be placed here. + * Static utility methods used by annotation abstractions in this package. Some methods in this + * class depend on the use of Sun javac internals; any procedure in the Checker Framework that uses + * a non-public API should be placed here. */ public class InternalUtils { @@ -64,11 +65,9 @@ public class InternalUtils { * Gets the {@link Element} ("symbol") for the given Tree API node. * * @param tree the {@link Tree} node to get the symbol for - * @throws IllegalArgumentException - * if {@code tree} is null or is not a valid javac-internal tree - * (JCTree) - * @return the {@code {@link Symbol}} for the given tree, or null if one - * could not be found + * @throws IllegalArgumentException if {@code tree} is null or is not a valid javac-internal + * tree (JCTree) + * @return the {@link Symbol} for the given tree, or null if one could not be found */ public static /*@Nullable*/ Element symbol(Tree tree) { if (tree == null) { @@ -95,19 +94,19 @@ public class InternalUtils { case TYPE_PARAMETER: return TreeInfo.symbolFor((JCTree) tree); - // symbol() only works on MethodSelects, so we need to get it manually - // for method invocations. + // symbol() only works on MethodSelects, so we need to get it manually + // for method invocations. case METHOD_INVOCATION: return TreeInfo.symbol(((JCMethodInvocation) tree).getMethodSelect()); case ASSIGNMENT: - return TreeInfo.symbol((JCTree)((AssignmentTree)tree).getVariable()); + return TreeInfo.symbol((JCTree) ((AssignmentTree) tree).getVariable()); case ARRAY_ACCESS: - return symbol(((ArrayAccessTree)tree).getExpression()); + return symbol(((ArrayAccessTree) tree).getExpression()); case NEW_CLASS: - return ((JCNewClass)tree).constructor; + return ((JCNewClass) tree).constructor; case MEMBER_REFERENCE: // TreeInfo.symbol, which is used in the default case, didn't handle @@ -120,43 +119,40 @@ public class InternalUtils { } /** - * Determines whether or not the node referred to by the given - * {@link TreePath} is an anonymous constructor (the constructor for an - * anonymous class. + * Determines whether or not the node referred to by the given {@link TreePath} is an anonymous + * constructor (the constructor for an anonymous class. * - * @param method the {@link TreePath} for a node that may be an anonymous - * constructor - * @return true if the given path points to an anonymous constructor, false - * if it does not + * @param method the {@link TreePath} for a node that may be an anonymous constructor + * @return true if the given path points to an anonymous constructor, false if it does not */ public static boolean isAnonymousConstructor(final MethodTree method) { /*@Nullable*/ Element e = InternalUtils.symbol(method); - if (e == null || !(e instanceof Symbol)) + if (e == null || !(e instanceof Symbol)) { return false; + } - if ((((/*@NonNull*/ Symbol)e).flags() & Flags.ANONCONSTR) != 0) + if ((((/*@NonNull*/ Symbol) e).flags() & Flags.ANONCONSTR) != 0) { return true; + } return false; } /** - * indicates whether it should return the constructor that gets invoked - * in cases of anonymous classes + * indicates whether it should return the constructor that gets invoked in cases of anonymous + * classes */ private static final boolean RETURN_INVOKE_CONSTRUCTOR = true; /** - * Determines the symbol for a constructor given an invocation via - * {@code new}. + * Determines the symbol for a constructor given an invocation via {@code new}. * - * If the tree is a declaration of an anonymous class, then method returns - * constructor that gets invoked in the extended class, rather than the - * anonymous constructor implicitly added by the constructor (JLS 15.9.5.1) + * <p>If the tree is a declaration of an anonymous class, then method returns constructor that + * gets invoked in the extended class, rather than the anonymous constructor implicitly added by + * the constructor (JLS 15.9.5.1) * * @param tree the constructor invocation - * @return the {@link ExecutableElement} corresponding to the constructor - * call in {@code tree} + * @return the {@link ExecutableElement} corresponding to the constructor call in {@code tree} */ public static ExecutableElement constructor(NewClassTree tree) { @@ -178,7 +174,7 @@ public class InternalUtils { // the method call is guaranteed to return nonnull JCMethodDecl anonConstructor = - (JCMethodDecl) TreeInfo.declarationFor(newClassTree.constructor, newClassTree); + (JCMethodDecl) TreeInfo.declarationFor(newClassTree.constructor, newClassTree); assert anonConstructor != null; assert anonConstructor.body.stats.size() == 1; JCExpressionStatement stmt = (JCExpressionStatement) anonConstructor.body.stats.head; @@ -193,22 +189,31 @@ public class InternalUtils { return (ExecutableElement) e; } - public final static List<AnnotationMirror> annotationsFromTypeAnnotationTrees(List<? extends AnnotationTree> annos) { + public static final List<AnnotationMirror> annotationsFromTypeAnnotationTrees( + List<? extends AnnotationTree> annos) { List<AnnotationMirror> annotations = new ArrayList<AnnotationMirror>(annos.size()); - for (AnnotationTree anno : annos) - annotations.add(((JCAnnotation)anno).attribute); + for (AnnotationTree anno : annos) { + annotations.add(annotationFromAnnotationTree(anno)); + } return annotations; } - public final static List<? extends AnnotationMirror> annotationsFromTree(AnnotatedTypeTree node) { - return annotationsFromTypeAnnotationTrees(((JCAnnotatedType)node).annotations); + public static AnnotationMirror annotationFromAnnotationTree(AnnotationTree tree) { + return ((JCAnnotation) tree).attribute; + } + + public static final List<? extends AnnotationMirror> annotationsFromTree( + AnnotatedTypeTree node) { + return annotationsFromTypeAnnotationTrees(((JCAnnotatedType) node).annotations); } - public final static List<? extends AnnotationMirror> annotationsFromTree(TypeParameterTree node) { - return annotationsFromTypeAnnotationTrees(((JCTypeParameter)node).annotations); + public static final List<? extends AnnotationMirror> annotationsFromTree( + TypeParameterTree node) { + return annotationsFromTypeAnnotationTrees(((JCTypeParameter) node).annotations); } - public final static List<? extends AnnotationMirror> annotationsFromArrayCreation(NewArrayTree node, int level) { + public static final List<? extends AnnotationMirror> annotationsFromArrayCreation( + NewArrayTree node, int level) { assert node instanceof JCNewArray; final JCNewArray newArray = ((JCNewArray) node); @@ -229,32 +234,40 @@ public class InternalUtils { return ((JCTree) tree).type; } - /** - * Returns whether a TypeVariable represents a captured type. - */ + /** Returns whether a TypeVariable represents a captured type. */ public static boolean isCaptured(TypeVariable typeVar) { - return ((Type.TypeVar) typeVar).isCaptured(); + return ((Type.TypeVar) TypeAnnotationUtils.unannotatedType(typeVar)).isCaptured(); } - /** - * Returns whether a TypeMirror represents a class type. - */ + /** If typeVar is a captured wildcard, returns that wildcard; otherwise returns null. */ + public static WildcardType getCapturedWildcard(TypeVariable typeVar) { + if (isCaptured(typeVar)) { + return ((CapturedType) TypeAnnotationUtils.unannotatedType(typeVar)).wildcard; + } + return null; + } + + /** Returns whether a TypeMirror represents a class type. */ public static boolean isClassType(TypeMirror type) { return (type instanceof Type.ClassType); } /** - * Returns the least upper bound of two {@link TypeMirror}s. + * Returns the least upper bound of two {@link TypeMirror}s, ignoring any annotations on the + * types. * - * @param processingEnv The {@link ProcessingEnvironment} to use. - * @param tm1 A {@link TypeMirror}. - * @param tm2 A {@link TypeMirror}. - * @return The least upper bound of {@code tm1} and {@code tm2}. + * <p>Wrapper around Types.lub to add special handling for null types, primitives, and + * wildcards. + * + * @param processingEnv the {@link ProcessingEnvironment} to use + * @param tm1 a {@link TypeMirror} + * @param tm2 a {@link TypeMirror} + * @return the least upper bound of {@code tm1} and {@code tm2}. */ public static TypeMirror leastUpperBound( ProcessingEnvironment processingEnv, TypeMirror tm1, TypeMirror tm2) { - Type t1 = (Type) tm1; - Type t2 = (Type) tm2; + Type t1 = TypeAnnotationUtils.unannotatedType(tm1); + Type t2 = TypeAnnotationUtils.unannotatedType(tm2); JavacProcessingEnvironment javacEnv = (JavacProcessingEnvironment) processingEnv; Types types = Types.instance(javacEnv.getContext()); if (types.isSameType(t1, t2)) { @@ -268,16 +281,6 @@ public class InternalUtils { if (t2.getKind() == TypeKind.NULL) { return t1; } - // Special case for primitives. - if (TypesUtils.isPrimitive(t1) || TypesUtils.isPrimitive(t2)) { - if (types.isAssignable(t1, t2)) { - return t2; - } else if (types.isAssignable(t2, t1)) { - return t1; - } else { - return processingEnv.getTypeUtils().getNoType(TypeKind.NONE); - } - } if (t1.getKind() == TypeKind.WILDCARD) { WildcardType wc1 = (WildcardType) t1; Type bound = (Type) wc1.getExtendsBound(); @@ -298,21 +301,36 @@ public class InternalUtils { } t2 = bound; } + // Special case for primitives. + if (TypesUtils.isPrimitive(t1) || TypesUtils.isPrimitive(t2)) { + if (types.isAssignable(t1, t2)) { + return t2; + } else if (types.isAssignable(t2, t1)) { + return t1; + } else { + Elements elements = processingEnv.getElementUtils(); + return elements.getTypeElement("java.lang.Object").asType(); + } + } return types.lub(t1, t2); } /** - * Returns the greatest lower bound of two {@link TypeMirror}s. + * Returns the greatest lower bound of two {@link TypeMirror}s, ignoring any annotations on the + * types. * - * @param processingEnv The {@link ProcessingEnvironment} to use. - * @param tm1 A {@link TypeMirror}. - * @param tm2 A {@link TypeMirror}. - * @return The greatest lower bound of {@code tm1} and {@code tm2}. + * <p>Wrapper around Types.glb to add special handling for null types, primitives, and + * wildcards. + * + * @param processingEnv the {@link ProcessingEnvironment} to use + * @param tm1 a {@link TypeMirror} + * @param tm2 a {@link TypeMirror} + * @return the greatest lower bound of {@code tm1} and {@code tm2}. */ public static TypeMirror greatestLowerBound( ProcessingEnvironment processingEnv, TypeMirror tm1, TypeMirror tm2) { - Type t1 = (Type) tm1; - Type t2 = (Type) tm2; + Type t1 = TypeAnnotationUtils.unannotatedType(tm1); + Type t2 = TypeAnnotationUtils.unannotatedType(tm2); JavacProcessingEnvironment javacEnv = (JavacProcessingEnvironment) processingEnv; Types types = Types.instance(javacEnv.getContext()); if (types.isSameType(t1, t2)) { @@ -345,21 +363,24 @@ public class InternalUtils { if (t2.getKind() == TypeKind.WILDCARD) { return t1; } + + // If neither type is a primitive type, null type, or wildcard + // and if the types are not the same, use javac types.glb return types.glb(t1, t2); } /** - * Returns the return type of a method, where the "raw" return type of that - * method is given (i.e., the return type might still contain unsubstituted - * type variables), given the receiver of the method call. + * Returns the return type of a method, where the "raw" return type of that method is given + * (i.e., the return type might still contain unsubstituted type variables), given the receiver + * of the method call. */ - public static TypeMirror substituteMethodReturnType(TypeMirror methodType, - TypeMirror substitutedReceiverType) { + public static TypeMirror substituteMethodReturnType( + TypeMirror methodType, TypeMirror substitutedReceiverType) { if (methodType.getKind() != TypeKind.TYPEVAR) { return methodType; } // TODO: find a nicer way to substitute type variables - String t = methodType.toString(); + String t = TypeAnnotationUtils.unannotatedType(methodType).toString(); Type finalReceiverType = (Type) substitutedReceiverType; int i = 0; for (TypeSymbol typeParam : finalReceiverType.tsym.getTypeParameters()) { @@ -372,13 +393,91 @@ public class InternalUtils { return null; } - /** Helper function to extract the javac Context from the - * javac processing environment. + /** + * Helper function to extract the javac Context from the javac processing environment. * * @param env the processing environment * @return the javac Context */ public static Context getJavacContext(ProcessingEnvironment env) { - return ((JavacProcessingEnvironment)env).getContext(); + return ((JavacProcessingEnvironment) env).getContext(); + } + + /** + * Returns the type element for {@code type} if {@code type} is a class, interface, annotation + * type, or enum. Otherwise, returns null. + * + * @param type whose element is returned + * @return the type element for {@code type} if {@code type} is a class, interface, annotation + * type, or enum; otherwise, returns null + */ + public static TypeElement getTypeElement(TypeMirror type) { + Element element = ((Type) type).asElement(); + switch (element.getKind()) { + case ANNOTATION_TYPE: + case CLASS: + case ENUM: + case INTERFACE: + return (TypeElement) element; + default: + return null; + } + } + + /** + * Obtain the class loader for {@code clazz}. If that is not available, return the system class + * loader. + * + * @param clazz the class whose class loader to find + * @return the class loader used to {@code clazz}, or the system class loader, or null if both + * are unavailable + */ + public static ClassLoader getClassLoaderForClass(Class<? extends Object> clazz) { + ClassLoader classLoader = clazz.getClassLoader(); + return classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader; + } + + /** + * Compares tree1 to tree2 by the position at which a diagnostic (e.g., an error message) for + * the tree should be printed. + */ + public static int compareDiagnosticPosition(Tree tree1, Tree tree2) { + DiagnosticPosition pos1 = (DiagnosticPosition) tree1; + DiagnosticPosition pos2 = (DiagnosticPosition) tree2; + + int preferred = Integer.compare(pos1.getPreferredPosition(), pos2.getPreferredPosition()); + if (preferred != 0) { + return preferred; + } + + return Integer.compare(pos1.getStartPosition(), pos2.getStartPosition()); + } + + /** + * Returns whether or not {@code type} is a functional interface type (as defined in JLS 9.8). + * + * @param type possible functional interface type + * @param env ProcessingEnvironment + * @return whether or not {@code type} is a functional interface type (as defined in JLS 9.8) + */ + public static boolean isFunctionalInterface(TypeMirror type, ProcessingEnvironment env) { + Context ctx = ((JavacProcessingEnvironment) env).getContext(); + com.sun.tools.javac.code.Types javacTypes = com.sun.tools.javac.code.Types.instance(ctx); + return javacTypes.isFunctionalInterface((Type) type); + } + + /** + * The type of the lambda or method reference tree is a functional interface type. This method + * returns the single abstract method declared by that functional interface. (The type of this + * method is referred to as the function type.) + * + * @param tree lambda or member reference tree + * @param env ProcessingEnvironment + * @return the single abstract method declared by the type of the tree + */ + public static Symbol findFunction(Tree tree, ProcessingEnvironment env) { + Context ctx = ((JavacProcessingEnvironment) env).getContext(); + com.sun.tools.javac.code.Types javacTypes = com.sun.tools.javac.code.Types.instance(ctx); + return javacTypes.findDescriptorSymbol(((Type) typeOf(tree)).asElement()); } } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Pair.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Pair.java index 779c9b34ec..161af82605 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Pair.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Pair.java @@ -8,7 +8,7 @@ import org.checkerframework.dataflow.qual.SideEffectFree; /** * Simple pair class for multiple returns. * - * TODO: as class is immutable, use @Covariant annotation. + * <p>TODO: as class is immutable, use @Covariant annotation. */ public class Pair<V1, V2> { public final V1 first; @@ -36,18 +36,21 @@ public class Pair<V1, V2> { public int hashCode() { if (hashCode == -1) { hashCode = 31; - if (first != null) + if (first != null) { hashCode += 17 * first.hashCode(); - if (second != null) + } + if (second != null) { hashCode += 17 * second.hashCode(); + } } return hashCode; } @Override public boolean equals(Object o) { - if (!(o instanceof Pair)) + if (!(o instanceof Pair)) { return false; + } @SuppressWarnings("unchecked") Pair<V1, V2> other = (Pair<V1, V2>) o; if (this.first == null) { diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java index c47566bfa2..361c6aef99 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java @@ -1,22 +1,15 @@ package org.checkerframework.javacutil; +import static com.sun.tools.javac.code.Kinds.PCK; +import static com.sun.tools.javac.code.Kinds.TYP; import static com.sun.tools.javac.code.Kinds.VAR; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; - import com.sun.source.util.TreePath; import com.sun.source.util.Trees; import com.sun.tools.javac.api.JavacScope; import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.ClassSymbol; +import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.comp.AttrContext; @@ -29,81 +22,184 @@ import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Names; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; -/** - * A Utility class to find symbols corresponding to string references. - */ +/** A Utility class to find symbols corresponding to string references. */ public class Resolver { private final Resolve resolve; private final Names names; private final Trees trees; private final Log log; - private final Method FIND_METHOD; - private final Method FIND_IDENT_IN_TYPE; - private final Method FIND_IDENT_IN_PACKAGE; - private final Method FIND_TYPE; + private static final Method FIND_METHOD; + private static final Method FIND_VAR; + private static final Method FIND_IDENT; + private static final Method FIND_IDENT_IN_TYPE; + private static final Method FIND_IDENT_IN_PACKAGE; + private static final Method FIND_TYPE; - public Resolver(ProcessingEnvironment env) { - Context context = ((JavacProcessingEnvironment) env).getContext(); - this.resolve = Resolve.instance(context); - this.names = Names.instance(context); - this.trees = Trees.instance(env); - this.log = Log.instance(context); + private static final Class<?> ACCESSERROR; + // Note that currently access(...) is defined in InvalidSymbolError, a superclass of AccessError + private static final Method ACCESSERROR_ACCESS; + static { try { - FIND_METHOD = Resolve.class.getDeclaredMethod("findMethod", - Env.class, Type.class, Name.class, List.class, List.class, - boolean.class, boolean.class, boolean.class); + FIND_METHOD = + Resolve.class.getDeclaredMethod( + "findMethod", + Env.class, + Type.class, + Name.class, + List.class, + List.class, + boolean.class, + boolean.class, + boolean.class); FIND_METHOD.setAccessible(true); - FIND_IDENT_IN_TYPE = Resolve.class.getDeclaredMethod( - "findIdentInType", Env.class, Type.class, Name.class, - int.class); + FIND_VAR = Resolve.class.getDeclaredMethod("findVar", Env.class, Name.class); + FIND_VAR.setAccessible(true); + + FIND_IDENT = + Resolve.class.getDeclaredMethod("findIdent", Env.class, Name.class, int.class); + FIND_IDENT.setAccessible(true); + + FIND_IDENT_IN_TYPE = + Resolve.class.getDeclaredMethod( + "findIdentInType", Env.class, Type.class, Name.class, int.class); FIND_IDENT_IN_TYPE.setAccessible(true); - FIND_IDENT_IN_PACKAGE = Resolve.class.getDeclaredMethod( - "findIdentInPackage", Env.class, TypeSymbol.class, Name.class, - int.class); + FIND_IDENT_IN_PACKAGE = + Resolve.class.getDeclaredMethod( + "findIdentInPackage", + Env.class, + TypeSymbol.class, + Name.class, + int.class); FIND_IDENT_IN_PACKAGE.setAccessible(true); - FIND_TYPE = Resolve.class.getDeclaredMethod( - "findType", Env.class, Name.class); + FIND_TYPE = Resolve.class.getDeclaredMethod("findType", Env.class, Name.class); FIND_TYPE.setAccessible(true); } catch (Exception e) { - Error err = new AssertionError( - "Compiler 'Resolve' class doesn't contain required 'find' method"); + Error err = + new AssertionError( + "Compiler 'Resolve' class doesn't contain required 'find' method"); err.initCause(e); throw err; } + + try { + ACCESSERROR = Class.forName("com.sun.tools.javac.comp.Resolve$AccessError"); + ACCESSERROR_ACCESS = ACCESSERROR.getMethod("access", Name.class, TypeSymbol.class); + ACCESSERROR_ACCESS.setAccessible(true); + } catch (ClassNotFoundException e) { + ErrorReporter.errorAbort( + "Compiler 'Resolve$AccessError' class could not be retrieved.", e); + // Unreachable code - needed so the compiler does not warn about a possibly uninitialized final field. + throw new AssertionError(); + } catch (NoSuchMethodException e) { + ErrorReporter.errorAbort( + "Compiler 'Resolve$AccessError' class doesn't contain required 'access' method", + e); + // Unreachable code - needed so the compiler does not warn about a possibly uninitialized final field. + throw new AssertionError(); + } + } + + public Resolver(ProcessingEnvironment env) { + Context context = ((JavacProcessingEnvironment) env).getContext(); + this.resolve = Resolve.instance(context); + this.names = Names.instance(context); + this.trees = Trees.instance(env); + this.log = Log.instance(context); + } + + /** + * Determine the environment for the given path. + * + * @param path the tree path to the local scope + * @return the corresponding attribution environment + */ + public Env<AttrContext> getEnvForPath(TreePath path) { + TreePath iter = path; + JavacScope scope = null; + while (scope == null && iter != null) { + try { + scope = (JavacScope) trees.getScope(iter); + } catch (Throwable t) { + // Work around Issue #1059 by skipping through the TreePath until something + // doesn't crash. This probably returns the class scope, so users might not + // get the variables they expect. But that is better than crashing. + iter = iter.getParentPath(); + } + } + if (scope != null) { + return scope.getEnv(); + } else { + ErrorReporter.errorAbort( + "Could not determine any possible scope for path: " + path.getLeaf()); + return null; + } + } + + /** + * Finds the package with name {@code name}. + * + * @param name the name of the package + * @param path the tree path to the local scope + * @return the {@code PackageSymbol} for the package if it is found, {@code null} otherwise + */ + public PackageSymbol findPackage(String name, TreePath path) { + Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log); + try { + Env<AttrContext> env = getEnvForPath(path); + Element res = + wrapInvocationOnResolveInstance(FIND_IDENT, env, names.fromString(name), PCK); + // findIdent will return a PackageSymbol even for a symbol that is not a package, + // such as a.b.c.MyClass.myStaticField. "exists()" must be called on it to ensure + // that it exists. + if (res.getKind() == ElementKind.PACKAGE) { + PackageSymbol ps = (PackageSymbol) res; + return ps.exists() ? ps : null; + } else { + return null; + } + } finally { + log.popDiagnosticHandler(discardDiagnosticHandler); + } } /** * Finds the field with name {@code name} in a given type. * - * <p> - * The method adheres to all the rules of Java's scoping (while also - * considering the imports) for name resolution. + * <p>The method adheres to all the rules of Java's scoping (while also considering the imports) + * for name resolution. * - * @param name - * The name of the field. - * @param type - * The type of the receiver (i.e., the type in which to look for - * the field). - * @param path - * The tree path to the local scope. - * @return The element for the field. + * @param name the name of the field + * @param type the type of the receiver (i.e., the type in which to look for the field). + * @param path the tree path to the local scope + * @return the element for the field */ public VariableElement findField(String name, TypeMirror type, TreePath path) { - Log.DiagnosticHandler discardDiagnosticHandler = - new Log.DiscardDiagnosticHandler(log); + Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log); try { - JavacScope scope = (JavacScope) trees.getScope(path); - Env<AttrContext> env = scope.getEnv(); - Element res = wrapInvocation(FIND_IDENT_IN_TYPE, env, type, - names.fromString(name), VAR); + Env<AttrContext> env = getEnvForPath(path); + Element res = + wrapInvocationOnResolveInstance( + FIND_IDENT_IN_TYPE, env, type, names.fromString(name), VAR); if (res.getKind() == ElementKind.FIELD) { return (VariableElement) res; + } else if (res.getKind() == ElementKind.OTHER && ACCESSERROR.isInstance(res)) { + // Return the inaccessible field that was found + return (VariableElement) wrapInvocation(res, ACCESSERROR_ACCESS, null, null); } else { // Most likely didn't find the field and the Element is a SymbolNotFoundError return null; @@ -114,53 +210,94 @@ public class Resolver { } /** + * Finds the local variable with name {@code name} in the given scope. + * + * @param name the name of the local variable + * @param path the tree path to the local scope + * @return the element for the local variable + */ + public VariableElement findLocalVariableOrParameterOrField(String name, TreePath path) { + Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log); + try { + Env<AttrContext> env = getEnvForPath(path); + Element res = wrapInvocationOnResolveInstance(FIND_VAR, env, names.fromString(name)); + if (res.getKind() == ElementKind.LOCAL_VARIABLE + || res.getKind() == ElementKind.PARAMETER + || res.getKind() == ElementKind.FIELD) { + return (VariableElement) res; + } else { + // Most likely didn't find the variable and the Element is a SymbolNotFoundError + return null; + } + } finally { + log.popDiagnosticHandler(discardDiagnosticHandler); + } + } + + /** * Finds the class literal with name {@code name}. * - * <p> - * The method adheres to all the rules of Java's scoping (while also - * considering the imports) for name resolution. + * <p>The method adheres to all the rules of Java's scoping (while also considering the imports) + * for name resolution. * - * @param name - * The name of the class. - * @param path - * The tree path to the local scope. - * @return The element for the class. + * @param name the name of the class + * @param path the tree path to the local scope + * @return the element for the class */ public Element findClass(String name, TreePath path) { - Log.DiagnosticHandler discardDiagnosticHandler = - new Log.DiscardDiagnosticHandler(log); + Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log); + try { + Env<AttrContext> env = getEnvForPath(path); + return wrapInvocationOnResolveInstance(FIND_TYPE, env, names.fromString(name)); + } finally { + log.popDiagnosticHandler(discardDiagnosticHandler); + } + } + + /** + * Finds the class with name {@code name} in a given package. + * + * @param name the name of the class + * @param pck the PackageSymbol for the package + * @param path the tree path to the local scope + * @return the {@code ClassSymbol} for the class if it is found, {@code null} otherwise + */ + public ClassSymbol findClassInPackage(String name, PackageSymbol pck, TreePath path) { + Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log); try { - JavacScope scope = (JavacScope) trees.getScope(path); - Env<AttrContext> env = scope.getEnv(); - return wrapInvocation(FIND_TYPE, env, names.fromString(name)); + Env<AttrContext> env = getEnvForPath(path); + Element res = + wrapInvocationOnResolveInstance( + FIND_IDENT_IN_PACKAGE, env, pck, names.fromString(name), TYP); + if (res.getKind() == ElementKind.CLASS) { + return (ClassSymbol) res; + } else { + return null; + } } finally { log.popDiagnosticHandler(discardDiagnosticHandler); } } /** - * Finds the method element for a given name and list of expected parameter - * types. + * Finds the method element for a given name and list of expected parameter types. * - * <p> - * The method adheres to all the rules of Java's scoping (while also - * considering the imports) for name resolution. + * <p>The method adheres to all the rules of Java's scoping (while also considering the imports) + * for name resolution. * - * @param methodName - * Name of the method to find. - * @param receiverType - * Type of the receiver of the method - * @param path - * Tree path. - * @return The method element (if found). + * @param methodName name of the method to find + * @param receiverType type of the receiver of the method + * @param path tree path + * @return the method element (if found) */ - public Element findMethod(String methodName, TypeMirror receiverType, - TreePath path, java.util.List<TypeMirror> argumentTypes) { - Log.DiagnosticHandler discardDiagnosticHandler = - new Log.DiscardDiagnosticHandler(log); + public Element findMethod( + String methodName, + TypeMirror receiverType, + TreePath path, + java.util.List<TypeMirror> argumentTypes) { + Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log); try { - JavacScope scope = (JavacScope) trees.getScope(path); - Env<AttrContext> env = scope.getEnv(); + Env<AttrContext> env = getEnvForPath(path); Type site = (Type) receiverType; Name name = names.fromString(methodName); @@ -179,8 +316,17 @@ public class Resolver { Object methodContext = buildMethodContext(); Object oldContext = getField(resolve, "currentResolutionContext"); setField(resolve, "currentResolutionContext", methodContext); - Element result = wrapInvocation(FIND_METHOD, env, site, name, argtypes, - typeargtypes, allowBoxing, useVarargs, operator); + Element result = + wrapInvocationOnResolveInstance( + FIND_METHOD, + env, + site, + name, + argtypes, + typeargtypes, + allowBoxing, + useVarargs, + operator); setField(resolve, "currentResolutionContext", oldContext); return result; } catch (Throwable t) { @@ -193,14 +339,13 @@ public class Resolver { } } - /** - * Build an instance of {@code Resolve$MethodResolutionContext}. - */ - protected Object buildMethodContext() throws ClassNotFoundException, - InstantiationException, IllegalAccessException, - InvocationTargetException, NoSuchFieldException { + /** Build an instance of {@code Resolve$MethodResolutionContext}. */ + protected Object buildMethodContext() + throws ClassNotFoundException, InstantiationException, IllegalAccessException, + InvocationTargetException, NoSuchFieldException { // Class is not accessible, instantiate reflectively. - Class<?> methCtxClss = Class.forName("com.sun.tools.javac.comp.Resolve$MethodResolutionContext"); + Class<?> methCtxClss = + Class.forName("com.sun.tools.javac.comp.Resolve$MethodResolutionContext"); Constructor<?> constructor = methCtxClss.getDeclaredConstructors()[0]; constructor.setAccessible(true); Object methodContext = constructor.newInstance(resolve); @@ -213,25 +358,28 @@ public class Resolver { } /** Reflectively set a field. */ - private void setField(Object receiver, String fieldName, - Object value) throws NoSuchFieldException, - IllegalAccessException { + private void setField(Object receiver, String fieldName, Object value) + throws NoSuchFieldException, IllegalAccessException { Field f = receiver.getClass().getDeclaredField(fieldName); f.setAccessible(true); f.set(receiver, value); } /** Reflectively get the value of a field. */ - private Object getField(Object receiver, String fieldName) throws NoSuchFieldException, - IllegalAccessException { + private Object getField(Object receiver, String fieldName) + throws NoSuchFieldException, IllegalAccessException { Field f = receiver.getClass().getDeclaredField(fieldName); f.setAccessible(true); return f.get(receiver); } - private Symbol wrapInvocation(Method method, Object... args) { + private Symbol wrapInvocationOnResolveInstance(Method method, Object... args) { + return wrapInvocation(resolve, method, args); + } + + private Symbol wrapInvocation(Object receiver, Method method, Object... args) { try { - return (Symbol) method.invoke(resolve, args); + return (Symbol) method.invoke(receiver, args); } catch (IllegalAccessException e) { Error err = new AssertionError("Unexpected Reflection error"); err.initCause(e); diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TreeUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TreeUtils.java index a3ae88fa31..4da97b400d 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TreeUtils.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TreeUtils.java @@ -4,25 +4,13 @@ package org.checkerframework.javacutil; import org.checkerframework.checker.nullness.qual.*; */ -import java.util.EnumSet; -import java.util.Set; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Name; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.util.ElementFilter; - import com.sun.source.tree.AnnotatedTypeTree; import com.sun.source.tree.ArrayAccessTree; -import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.BinaryTree; import com.sun.source.tree.BlockTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompoundAssignmentTree; +import com.sun.source.tree.ConditionalExpressionTree; import com.sun.source.tree.ExpressionStatementTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.IdentifierTree; @@ -30,14 +18,13 @@ import com.sun.source.tree.LiteralTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.MethodTree; -import com.sun.source.tree.NewArrayTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.ParameterizedTypeTree; import com.sun.source.tree.ParenthesizedTree; import com.sun.source.tree.PrimitiveTypeTree; -import com.sun.source.tree.ReturnTree; import com.sun.source.tree.StatementTree; import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.TypeCastTree; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePath; @@ -45,21 +32,34 @@ import com.sun.source.util.Trees; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.tree.JCTree; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; +import javax.lang.model.util.ElementFilter; -/** - * A utility class made for helping to analyze a given {@code Tree}. - */ +/** A utility class made for helping to analyze a given {@code Tree}. */ // TODO: This class needs significant restructuring public final class TreeUtils { // Class cannot be instantiated. - private TreeUtils() { throw new AssertionError("Class TreeUtils cannot be instantiated."); } + private TreeUtils() { + throw new AssertionError("Class TreeUtils cannot be instantiated."); + } /** * Checks if the provided method is a constructor method or no. * - * @param tree - * a tree defining the method + * @param tree a tree defining the method * @return true iff tree describes a constructor */ public static boolean isConstructor(final MethodTree tree) { @@ -69,39 +69,50 @@ public final class TreeUtils { /** * Checks if the method invocation is a call to super. * - * @param tree - * a tree defining a method invocation - * + * @param tree a tree defining a method invocation * @return true iff tree describes a call to super */ public static boolean isSuperCall(MethodInvocationTree tree) { + return isNamedMethodCall("super", tree); + } + + /** + * Checks if the method invocation is a call to this. + * + * @param tree a tree defining a method invocation + * @return true iff tree describes a call to this + */ + public static boolean isThisCall(MethodInvocationTree tree) { + return isNamedMethodCall("this", tree); + } + + protected static boolean isNamedMethodCall(String name, MethodInvocationTree tree) { /*@Nullable*/ ExpressionTree mst = tree.getMethodSelect(); assert mst != null; /*nninvariant*/ - if (mst.getKind() == Tree.Kind.IDENTIFIER ) { - return ((IdentifierTree)mst).getName().contentEquals("super"); + if (mst.getKind() == Tree.Kind.IDENTIFIER) { + return ((IdentifierTree) mst).getName().contentEquals(name); } if (mst.getKind() == Tree.Kind.MEMBER_SELECT) { - MemberSelectTree selectTree = (MemberSelectTree)mst; + MemberSelectTree selectTree = (MemberSelectTree) mst; if (selectTree.getExpression().getKind() != Tree.Kind.IDENTIFIER) { return false; } - return ((IdentifierTree) selectTree.getExpression()).getName() - .contentEquals("super"); + return ((IdentifierTree) selectTree.getExpression()).getName().contentEquals(name); } return false; } /** - * Returns true if the tree is a tree that 'looks like' either an access - * of a field or an invocation of a method that are owned by the same - * accessing instance. + * Returns true if the tree is a tree that 'looks like' either an access of a field or an + * invocation of a method that are owned by the same accessing instance. + * + * <p>It would only return true if the access tree is of the form: * - * It would only return true if the access tree is of the form: * <pre> * field * this.field @@ -110,35 +121,37 @@ public final class TreeUtils { * this.method() * </pre> * - * It does not perform any semantical check to differentiate between - * fields and local variables; local methods or imported static methods. + * It does not perform any semantical check to differentiate between fields and local variables; + * local methods or imported static methods. * - * @param tree expression tree representing an access to object member + * @param tree expression tree representing an access to object member * @return {@code true} iff the member is a member of {@code this} instance */ public static boolean isSelfAccess(final ExpressionTree tree) { ExpressionTree tr = TreeUtils.skipParens(tree); // If method invocation check the method select - if (tr.getKind() == Tree.Kind.ARRAY_ACCESS) + if (tr.getKind() == Tree.Kind.ARRAY_ACCESS) { return false; + } if (tree.getKind() == Tree.Kind.METHOD_INVOCATION) { - tr = ((MethodInvocationTree)tree).getMethodSelect(); + tr = ((MethodInvocationTree) tree).getMethodSelect(); } tr = TreeUtils.skipParens(tr); - if (tr.getKind() == Tree.Kind.TYPE_CAST) - tr = ((TypeCastTree)tr).getExpression(); + if (tr.getKind() == Tree.Kind.TYPE_CAST) { + tr = ((TypeCastTree) tr).getExpression(); + } tr = TreeUtils.skipParens(tr); - if (tr.getKind() == Tree.Kind.IDENTIFIER) + if (tr.getKind() == Tree.Kind.IDENTIFIER) { return true; + } if (tr.getKind() == Tree.Kind.MEMBER_SELECT) { - tr = ((MemberSelectTree)tr).getExpression(); + tr = ((MemberSelectTree) tr).getExpression(); if (tr.getKind() == Tree.Kind.IDENTIFIER) { - Name ident = ((IdentifierTree)tr).getName(); - return ident.contentEquals("this") || - ident.contentEquals("super"); + Name ident = ((IdentifierTree) tr).getName(); + return ident.contentEquals("this") || ident.contentEquals("super"); } } @@ -148,8 +161,8 @@ public final class TreeUtils { /** * Gets the first enclosing tree in path, of the specified kind. * - * @param path the path defining the tree node - * @param kind the kind of the desired tree + * @param path the path defining the tree node + * @param kind the kind of the desired tree * @return the enclosing tree of the given type as given by the path */ public static Tree enclosingOfKind(final TreePath path, final Tree.Kind kind) { @@ -159,8 +172,8 @@ public final class TreeUtils { /** * Gets the first enclosing tree in path, with any one of the specified kinds. * - * @param path the path defining the tree node - * @param kinds the set of kinds of the desired tree + * @param path the path defining the tree node + * @param kinds the set of kinds of the desired tree * @return the enclosing tree of the given type as given by the path */ public static Tree enclosingOfKind(final TreePath path, final Set<Tree.Kind> kinds) { @@ -169,8 +182,9 @@ public final class TreeUtils { while (p != null) { Tree leaf = p.getLeaf(); assert leaf != null; /*nninvariant*/ - if (kinds.contains(leaf.getKind())) + if (kinds.contains(leaf.getKind())) { return leaf; + } p = p.getParentPath(); } @@ -178,10 +192,10 @@ public final class TreeUtils { } /** - * Gets path to the the first enclosing class tree, where class is - * defined by the classTreeKinds method. + * Gets path to the first enclosing class tree, where class is defined by the classTreeKinds + * method. * - * @param path the path defining the tree node + * @param path the path defining the tree node * @return the path to the enclosing class tree */ public static TreePath pathTillClass(final TreePath path) { @@ -189,10 +203,10 @@ public final class TreeUtils { } /** - * Gets path to the the first enclosing tree of the specified kind. + * Gets path to the first enclosing tree of the specified kind. * - * @param path the path defining the tree node - * @param kind the kind of the desired tree + * @param path the path defining the tree node + * @param kind the kind of the desired tree * @return the path to the enclosing tree of the given type */ public static TreePath pathTillOfKind(final TreePath path, final Tree.Kind kind) { @@ -200,10 +214,10 @@ public final class TreeUtils { } /** - * Gets path to the the first enclosing tree with any one of the specified kinds. + * Gets path to the first enclosing tree with any one of the specified kinds. * - * @param path the path defining the tree node - * @param kinds the set of kinds of the desired tree + * @param path the path defining the tree node + * @param kinds the set of kinds of the desired tree * @return the path to the enclosing tree of the given type */ public static TreePath pathTillOfKind(final TreePath path, final Set<Tree.Kind> kinds) { @@ -212,8 +226,9 @@ public final class TreeUtils { while (p != null) { Tree leaf = p.getLeaf(); assert leaf != null; /*nninvariant*/ - if (kinds.contains(leaf.getKind())) + if (kinds.contains(leaf.getKind())) { return p; + } p = p.getParentPath(); } @@ -223,17 +238,19 @@ public final class TreeUtils { /** * Gets the first enclosing tree in path, of the specified class * - * @param path the path defining the tree node + * @param path the path defining the tree node * @param treeClass the class of the desired tree * @return the enclosing tree of the given type as given by the path */ - public static <T extends Tree> T enclosingOfClass(final TreePath path, final Class<T> treeClass) { + public static <T extends Tree> T enclosingOfClass( + final TreePath path, final Class<T> treeClass) { TreePath p = path; while (p != null) { Tree leaf = p.getLeaf(); - if (treeClass.isInstance(leaf)) + if (treeClass.isInstance(leaf)) { return treeClass.cast(leaf); + } p = p.getParentPath(); } @@ -241,22 +258,20 @@ public final class TreeUtils { } /** - * Gets the enclosing class of the tree node defined by the given - * {@code {@link TreePath}}. It returns a {@link Tree}, from which - * {@code checkers.types.AnnotatedTypeMirror} or {@link Element} can be + * Gets the enclosing class of the tree node defined by the given {@link TreePath}. It returns a + * {@link Tree}, from which {@code checkers.types.AnnotatedTypeMirror} or {@link Element} can be * obtained. * * @param path the path defining the tree node - * @return the enclosing class (or interface) as given by the path, or null - * if one does not exist. + * @return the enclosing class (or interface) as given by the path, or null if one does not + * exist */ public static /*@Nullable*/ ClassTree enclosingClass(final /*@Nullable*/ TreePath path) { return (ClassTree) enclosingOfKind(path, classTreeKinds()); } /** - * Gets the enclosing variable of a tree node defined by the given - * {@link TreePath}. + * Gets the enclosing variable of a tree node defined by the given {@link TreePath}. * * @param path the path defining the tree node * @return the enclosing variable as given by the path, or null if one does not exist @@ -266,14 +281,12 @@ public final class TreeUtils { } /** - * Gets the enclosing method of the tree node defined by the given - * {@code {@link TreePath}}. It returns a {@link Tree}, from which an - * {@code checkers.types.AnnotatedTypeMirror} or {@link Element} can be - * obtained. + * Gets the enclosing method of the tree node defined by the given {@link TreePath}. It returns + * a {@link Tree}, from which an {@code checkers.types.AnnotatedTypeMirror} or {@link Element} + * can be obtained. * * @param path the path defining the tree node - * @return the enclosing method as given by the path, or null if one does - * not exist + * @return the enclosing method as given by the path, or null if one does not exist */ public static /*@Nullable*/ MethodTree enclosingMethod(final /*@Nullable*/ TreePath path) { return (MethodTree) enclosingOfKind(path, Tree.Kind.METHOD); @@ -281,7 +294,7 @@ public final class TreeUtils { public static /*@Nullable*/ BlockTree enclosingTopLevelBlock(TreePath path) { TreePath parpath = path.getParentPath(); - while (parpath!=null && parpath.getLeaf().getKind() != Tree.Kind.CLASS) { + while (parpath != null && !classTreeKinds.contains(parpath.getLeaf().getKind())) { path = parpath; parpath = parpath.getParentPath(); } @@ -291,61 +304,89 @@ public final class TreeUtils { return null; } - /** - * If the given tree is a parenthesized tree, it returns the enclosed - * non-parenthesized tree. Otherwise, it returns the same tree. + * If the given tree is a parenthesized tree, it returns the enclosed non-parenthesized tree. + * Otherwise, it returns the same tree. * - * @param tree an expression tree - * @return the outermost non-parenthesized tree enclosed by the given tree + * @param tree an expression tree + * @return the outermost non-parenthesized tree enclosed by the given tree */ public static ExpressionTree skipParens(final ExpressionTree tree) { ExpressionTree t = tree; - while (t.getKind() == Tree.Kind.PARENTHESIZED) - t = ((ParenthesizedTree)t).getExpression(); + while (t.getKind() == Tree.Kind.PARENTHESIZED) { + t = ((ParenthesizedTree) t).getExpression(); + } return t; } /** - * Returns the tree with the assignment context for the treePath - * leaf node. + * Returns the tree with the assignment context for the treePath leaf node. (Does not handle + * pseudo-assignment of an argument to a parameter or a receiver expression to a receiver.) + * + * <p>The assignment context for the {@code treePath} is the leaf of its parent, if the parent + * is one of the following trees: * - * The assignment context for the treepath is the most enclosing - * tree of type: * <ul> - * <li>AssignmentTree </li> - * <li>CompoundAssignmentTree </li> - * <li>MethodInvocationTree</li> - * <li>NewArrayTree</li> - * <li>NewClassTree</li> - * <li>ReturnTree</li> - * <li>VariableTree</li> + * <li>AssignmentTree + * <li>CompoundAssignmentTree + * <li>MethodInvocationTree + * <li>NewArrayTree + * <li>NewClassTree + * <li>ReturnTree + * <li>VariableTree * </ul> * - * @param treePath - * @return the assignment context as described. + * If the parent is a ConditionalExpressionTree we need to distinguish two cases: If the leaf is + * either the then or else branch of the ConditionalExpressionTree, then recurse on the parent. + * If the leaf is the condition of the ConditionalExpressionTree, then return null to not + * consider this assignment context. + * + * <p>If the leaf is a ParenthesizedTree, then recurse on the parent. + * + * <p>Otherwise, null is returned. + * + * @return the assignment context as described */ public static Tree getAssignmentContext(final TreePath treePath) { - TreePath path = treePath.getParentPath(); + TreePath parentPath = treePath.getParentPath(); - if (path == null) + if (parentPath == null) { return null; - Tree node = path.getLeaf(); - if ((node instanceof AssignmentTree) || - (node instanceof CompoundAssignmentTree) || - (node instanceof MethodInvocationTree) || - (node instanceof NewArrayTree) || - (node instanceof NewClassTree) || - (node instanceof ReturnTree) || - (node instanceof VariableTree)) - return node; - return null; + } + + Tree parent = parentPath.getLeaf(); + switch (parent.getKind()) { + case PARENTHESIZED: + return getAssignmentContext(parentPath); + case CONDITIONAL_EXPRESSION: + ConditionalExpressionTree cet = (ConditionalExpressionTree) parent; + if (cet.getCondition() == treePath.getLeaf()) { + // The assignment context for the condition is simply boolean. + // No point in going on. + return null; + } + // Otherwise use the context of the ConditionalExpressionTree. + return getAssignmentContext(parentPath); + case ASSIGNMENT: + case METHOD_INVOCATION: + case NEW_ARRAY: + case NEW_CLASS: + case RETURN: + case VARIABLE: + return parent; + default: + // 11 Tree.Kinds are CompoundAssignmentTrees, + // so use instanceof rather than listing all 11. + if (parent instanceof CompoundAssignmentTree) { + return parent; + } + return null; + } } /** * Gets the element for a class corresponding to a declaration. * - * @param node * @return the element for the given class */ public static final TypeElement elementFromDeclaration(ClassTree node) { @@ -356,7 +397,6 @@ public final class TreeUtils { /** * Gets the element for a method corresponding to a declaration. * - * @param node * @return the element for the given method */ public static final ExecutableElement elementFromDeclaration(MethodTree node) { @@ -367,7 +407,6 @@ public final class TreeUtils { /** * Gets the element for a variable corresponding to its declaration. * - * @param node * @return the element for the given variable */ public static final VariableElement elementFromDeclaration(VariableTree node) { @@ -376,11 +415,10 @@ public final class TreeUtils { } /** - * Gets the element for the declaration corresponding to this use of an element. - * To get the element for a declaration, use {@link - * Trees#getElement(TreePath)} instead. + * Gets the element for the declaration corresponding to this use of an element. To get the + * element for a declaration, use {@link Trees#getElement(TreePath)} instead. * - * TODO: remove this method, as it really doesn't do anything. + * <p>TODO: remove this method, as it really doesn't do anything. * * @param node the tree corresponding to a use of an element * @return the element for the corresponding declaration @@ -390,8 +428,14 @@ public final class TreeUtils { } // Specialization for return type. + // Might return null if element wasn't found. public static final ExecutableElement elementFromUse(MethodInvocationTree node) { - return (ExecutableElement) elementFromUse((ExpressionTree) node); + Element el = elementFromUse((ExpressionTree) node); + if (el instanceof ExecutableElement) { + return (ExecutableElement) el; + } else { + return null; + } } // Specialization for return type. @@ -399,12 +443,11 @@ public final class TreeUtils { return (ExecutableElement) elementFromUse((ExpressionTree) node); } - /** * Determine whether the given ExpressionTree has an underlying element. * * @param node the ExpressionTree to test - * @return whether the tree refers to an identifier, member select, or method invocation. + * @return whether the tree refers to an identifier, member select, or method invocation */ public static final boolean isUseOfElement(ExpressionTree node) { node = TreeUtils.skipParens(node); @@ -419,35 +462,35 @@ public final class TreeUtils { } } - /** - * @return the name of the invoked method - */ + /** @return the name of the invoked method */ public static final Name methodName(MethodInvocationTree node) { ExpressionTree expr = node.getMethodSelect(); - if (expr.getKind() == Tree.Kind.IDENTIFIER) - return ((IdentifierTree)expr).getName(); - else if (expr.getKind() == Tree.Kind.MEMBER_SELECT) - return ((MemberSelectTree)expr).getIdentifier(); + if (expr.getKind() == Tree.Kind.IDENTIFIER) { + return ((IdentifierTree) expr).getName(); + } else if (expr.getKind() == Tree.Kind.MEMBER_SELECT) { + return ((MemberSelectTree) expr).getIdentifier(); + } ErrorReporter.errorAbort("TreeUtils.methodName: cannot be here: " + node); return null; // dead code } /** - * @return true if the first statement in the body is a self constructor - * invocation within a constructor + * @return true if the first statement in the body is a self constructor invocation within a + * constructor */ public static final boolean containsThisConstructorInvocation(MethodTree node) { - if (!TreeUtils.isConstructor(node) - || node.getBody().getStatements().isEmpty()) + if (!TreeUtils.isConstructor(node) || node.getBody().getStatements().isEmpty()) return false; StatementTree st = node.getBody().getStatements().get(0); if (!(st instanceof ExpressionStatementTree) - || !(((ExpressionStatementTree)st).getExpression() instanceof MethodInvocationTree)) + || !(((ExpressionStatementTree) st).getExpression() + instanceof MethodInvocationTree)) { return false; + } - MethodInvocationTree invocation = (MethodInvocationTree) - ((ExpressionStatementTree)st).getExpression(); + MethodInvocationTree invocation = + (MethodInvocationTree) ((ExpressionStatementTree) st).getExpression(); return "this".contentEquals(TreeUtils.methodName(invocation)); } @@ -455,11 +498,12 @@ public final class TreeUtils { public static final Tree firstStatement(Tree tree) { Tree first; if (tree.getKind() == Tree.Kind.BLOCK) { - BlockTree block = (BlockTree)tree; - if (block.getStatements().isEmpty()) + BlockTree block = (BlockTree) tree; + if (block.getStatements().isEmpty()) { first = block; - else + } else { first = block.getStatements().iterator().next(); + } } else { first = tree; } @@ -469,13 +513,13 @@ public final class TreeUtils { /** * Determine whether the given class contains an explicit constructor. * - * @param node A class tree. - * @return True, iff there is an explicit constructor. + * @param node a class tree + * @return true, iff there is an explicit constructor */ public static boolean hasExplicitConstructor(ClassTree node) { TypeElement elem = TreeUtils.elementFromDeclaration(node); - for ( ExecutableElement ee : ElementFilter.constructorsIn(elem.getEnclosedElements())) { + for (ExecutableElement ee : ElementFilter.constructorsIn(elem.getEnclosedElements())) { MethodSymbol ms = (MethodSymbol) ee; long mod = ms.flags(); @@ -487,33 +531,31 @@ public final class TreeUtils { } /** - * Returns true if the tree is of a diamond type. - * In contrast to the implementation in TreeInfo, this version - * works on Trees. + * Returns true if the tree is of a diamond type. In contrast to the implementation in TreeInfo, + * this version works on Trees. * * @see com.sun.tools.javac.tree.TreeInfo#isDiamond(JCTree) */ public static final boolean isDiamondTree(Tree tree) { switch (tree.getKind()) { - case ANNOTATED_TYPE: return isDiamondTree(((AnnotatedTypeTree)tree).getUnderlyingType()); - case PARAMETERIZED_TYPE: return ((ParameterizedTypeTree)tree).getTypeArguments().isEmpty(); - case NEW_CLASS: return isDiamondTree(((NewClassTree)tree).getIdentifier()); - default: return false; + case ANNOTATED_TYPE: + return isDiamondTree(((AnnotatedTypeTree) tree).getUnderlyingType()); + case PARAMETERIZED_TYPE: + return ((ParameterizedTypeTree) tree).getTypeArguments().isEmpty(); + case NEW_CLASS: + return isDiamondTree(((NewClassTree) tree).getIdentifier()); + default: + return false; } } - /** - * Returns true if the tree represents a {@code String} concatenation - * operation - */ + /** Returns true if the tree represents a {@code String} concatenation operation */ public static final boolean isStringConcatenation(Tree tree) { return (tree.getKind() == Tree.Kind.PLUS && TypesUtils.isString(InternalUtils.typeOf(tree))); } - /** - * Returns true if the compound assignment tree is a string concatenation - */ + /** Returns true if the compound assignment tree is a string concatenation */ public static final boolean isStringCompoundConcatenation(CompoundAssignmentTree tree) { return (tree.getKind() == Tree.Kind.PLUS_ASSIGNMENT && TypesUtils.isString(InternalUtils.typeOf(tree))); @@ -522,18 +564,19 @@ public final class TreeUtils { /** * Returns true if the node is a constant-time expression. * - * A tree is a constant-time expression if it is: + * <p>A tree is a constant-time expression if it is: + * * <ol> - * <li>a literal tree - * <li>a reference to a final variable initialized with a compile time - * constant - * <li>a String concatenation of two compile time constants + * <li>a literal tree + * <li>a reference to a final variable initialized with a compile time constant + * <li>a String concatenation of two compile time constants * </ol> */ public static boolean isCompileTimeString(ExpressionTree node) { ExpressionTree tree = TreeUtils.skipParens(node); - if (tree instanceof LiteralTree) + if (tree instanceof LiteralTree) { return true; + } if (TreeUtils.isUseOfElement(tree)) { Element elt = TreeUtils.elementFromUse(tree); @@ -541,15 +584,13 @@ public final class TreeUtils { } else if (TreeUtils.isStringConcatenation(tree)) { BinaryTree binOp = (BinaryTree) tree; return isCompileTimeString(binOp.getLeftOperand()) - && isCompileTimeString(binOp.getRightOperand()); + && isCompileTimeString(binOp.getRightOperand()); } else { return false; } } - /** - * Returns the receiver tree of a field access or a method invocation - */ + /** Returns the receiver tree of a field access or a method invocation */ public static ExpressionTree getReceiverTree(ExpressionTree expression) { ExpressionTree receiver = TreeUtils.skipParens(expression); @@ -565,13 +606,13 @@ public final class TreeUtils { // Trying to handle receiver calls to trees of the form // ((m).getArray()) // returns the type of 'm' in this case - receiver = ((MethodInvocationTree)receiver).getMethodSelect(); + receiver = ((MethodInvocationTree) receiver).getMethodSelect(); if (receiver.getKind() == Tree.Kind.IDENTIFIER) { // It's a method call "m(foo)" without an explicit receiver return null; } else if (receiver.getKind() == Tree.Kind.MEMBER_SELECT) { - receiver = ((MemberSelectTree)receiver).getExpression(); + receiver = ((MemberSelectTree) receiver).getExpression(); } else { // Otherwise, e.g. a NEW_CLASS: nothing to do. } @@ -579,9 +620,9 @@ public final class TreeUtils { // It's a field access on implicit this or a local variable/parameter. return null; } else if (receiver.getKind() == Tree.Kind.ARRAY_ACCESS) { - return TreeUtils.skipParens(((ArrayAccessTree)receiver).getExpression()); + return TreeUtils.skipParens(((ArrayAccessTree) receiver).getExpression()); } else if (receiver.getKind() == Tree.Kind.MEMBER_SELECT) { - receiver = ((MemberSelectTree)receiver).getExpression(); + receiver = ((MemberSelectTree) receiver).getExpression(); // Avoid int.class if (receiver instanceof PrimitiveTypeTree) { return null; @@ -596,20 +637,19 @@ public final class TreeUtils { // Adding Tree.Kind.NEW_CLASS here doesn't work, because then a // tree gets cast to ClassTree when it is actually a NewClassTree, // for example in enclosingClass above. - private final static Set<Tree.Kind> classTreeKinds = EnumSet.of( - Tree.Kind.CLASS, - Tree.Kind.ENUM, - Tree.Kind.INTERFACE, - Tree.Kind.ANNOTATION_TYPE - ); + private static final Set<Tree.Kind> classTreeKinds = + EnumSet.of( + Tree.Kind.CLASS, + Tree.Kind.ENUM, + Tree.Kind.INTERFACE, + Tree.Kind.ANNOTATION_TYPE); public static Set<Tree.Kind> classTreeKinds() { return classTreeKinds; } /** - * Is the given tree kind a class, i.e. a class, enum, - * interface, or annotation type. + * Is the given tree kind a class, i.e. a class, enum, interface, or annotation type. * * @param tree the tree to test * @return true, iff the given kind is a class kind @@ -618,16 +658,16 @@ public final class TreeUtils { return classTreeKinds().contains(tree.getKind()); } - private final static Set<Tree.Kind> typeTreeKinds = EnumSet.of( - Tree.Kind.PRIMITIVE_TYPE, - Tree.Kind.PARAMETERIZED_TYPE, - Tree.Kind.TYPE_PARAMETER, - Tree.Kind.ARRAY_TYPE, - Tree.Kind.UNBOUNDED_WILDCARD, - Tree.Kind.EXTENDS_WILDCARD, - Tree.Kind.SUPER_WILDCARD, - Tree.Kind.ANNOTATED_TYPE - ); + private static final Set<Tree.Kind> typeTreeKinds = + EnumSet.of( + Tree.Kind.PRIMITIVE_TYPE, + Tree.Kind.PARAMETERIZED_TYPE, + Tree.Kind.TYPE_PARAMETER, + Tree.Kind.ARRAY_TYPE, + Tree.Kind.UNBOUNDED_WILDCARD, + Tree.Kind.EXTENDS_WILDCARD, + Tree.Kind.SUPER_WILDCARD, + Tree.Kind.ANNOTATED_TYPE); public static Set<Tree.Kind> typeTreeKinds() { return typeTreeKinds; @@ -636,8 +676,8 @@ public final class TreeUtils { /** * Is the given tree a type instantiation? * - * TODO: this is an under-approximation: e.g. an identifier could - * be either a type use or an expression. How can we distinguish. + * <p>TODO: this is an under-approximation: e.g. an identifier could be either a type use or an + * expression. How can we distinguish. * * @param tree the tree to test * @return true, iff the given tree is a type @@ -647,54 +687,60 @@ public final class TreeUtils { } /** - * Returns true if the given element is an invocation of the method, or - * of any method that overrides that one. + * Returns true if the given element is an invocation of the method, or of any method that + * overrides that one. */ - public static boolean isMethodInvocation(Tree tree, ExecutableElement method, ProcessingEnvironment env) { - if (!(tree instanceof MethodInvocationTree)) + public static boolean isMethodInvocation( + Tree tree, ExecutableElement method, ProcessingEnvironment env) { + if (!(tree instanceof MethodInvocationTree)) { return false; - MethodInvocationTree methInvok = (MethodInvocationTree)tree; + } + MethodInvocationTree methInvok = (MethodInvocationTree) tree; ExecutableElement invoked = TreeUtils.elementFromUse(methInvok); - return isMethod(invoked, method, env); - } - - /** Returns true if the given element is, or overrides, method. */ - private static boolean isMethod(ExecutableElement questioned, ExecutableElement method, ProcessingEnvironment env) { - return (questioned.equals(method) - || env.getElementUtils().overrides(questioned, method, - (TypeElement)questioned.getEnclosingElement())); + return ElementUtils.isMethod(invoked, method, env); } /** - * Returns the ExecutableElement for a method declaration of - * methodName, in class typeName, with params parameters. + * Returns the ExecutableElement for a method declaration of methodName, in class typeName, with + * params parameters. * - * TODO: to precisely resolve method overloading, we should use parameter types and not just + * <p>TODO: to precisely resolve method overloading, we should use parameter types and not just * the number of parameters! */ - public static ExecutableElement getMethod(String typeName, String methodName, int params, ProcessingEnvironment env) { - TypeElement mapElt = env.getElementUtils().getTypeElement(typeName); - for (ExecutableElement exec : ElementFilter.methodsIn(mapElt.getEnclosedElements())) { + public static ExecutableElement getMethod( + String typeName, String methodName, int params, ProcessingEnvironment env) { + TypeElement typeElt = env.getElementUtils().getTypeElement(typeName); + for (ExecutableElement exec : ElementFilter.methodsIn(typeElt.getEnclosedElements())) { if (exec.getSimpleName().contentEquals(methodName) - && exec.getParameters().size() == params) + && exec.getParameters().size() == params) { return exec; + } } ErrorReporter.errorAbort("TreeUtils.getMethod: shouldn't be here!"); return null; // dead code } + public static List<ExecutableElement> getMethodList( + String typeName, String methodName, int params, ProcessingEnvironment env) { + List<ExecutableElement> methods = new ArrayList<>(); + TypeElement typeElement = env.getElementUtils().getTypeElement(typeName); + for (ExecutableElement exec : ElementFilter.methodsIn(typeElement.getEnclosedElements())) { + if (exec.getSimpleName().contentEquals(methodName) + && exec.getParameters().size() == params) { + methods.add(exec); + } + } + return methods; + } + /** - * Determine whether the given expression is either "this" or an outer - * "C.this". + * Determine whether the given expression is either "this" or an outer "C.this". * - * TODO: Should this also handle "super"? - * - * @param tree - * @return + * <p>TODO: Should this also handle "super"? */ public static final boolean isExplicitThisDereference(ExpressionTree tree) { if (tree.getKind() == Tree.Kind.IDENTIFIER - && ((IdentifierTree)tree).getName().contentEquals("this")) { + && ((IdentifierTree) tree).getName().contentEquals("this")) { // Explicit this reference "this" return true; } @@ -712,16 +758,30 @@ public final class TreeUtils { } /** - * Determine whether <code>tree</code> is a field access expressions, such - * as + * Determine whether {@code tree} is a class literal, such as + * + * <pre> + * <em>Object</em> . <em>class</em> + * </pre> + * + * @return true iff if tree is a class literal + */ + public static boolean isClassLiteral(Tree tree) { + if (tree.getKind() != Tree.Kind.MEMBER_SELECT) { + return false; + } + return "class".equals(((MemberSelectTree) tree).getIdentifier().toString()); + } + + /** + * Determine whether {@code tree} is a field access expressions, such as * * <pre> * <em>f</em> * <em>obj</em> . <em>f</em> * </pre> * - * @return true iff if tree is a field access expression (implicit or - * explicit). + * @return true iff if tree is a field access expression (implicit or explicit) */ public static boolean isFieldAccess(Tree tree) { if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) { @@ -734,17 +794,17 @@ public final class TreeUtils { IdentifierTree ident = (IdentifierTree) tree; Element el = TreeUtils.elementFromUse(ident); return el.getKind().isField() - && !ident.getName().contentEquals("this") && !ident.getName().contentEquals("super"); + && !ident.getName().contentEquals("this") + && !ident.getName().contentEquals("super"); } return false; } /** - * Compute the name of the field that the field access <code>tree</code> - * accesses. Requires <code>tree</code> to be a field access, as determined - * by <code>isFieldAccess</code>. + * Compute the name of the field that the field access {@code tree} accesses. Requires {@code + * tree} to be a field access, as determined by {@code isFieldAccess}. * - * @return The name of the field accessed by <code>tree</code>. + * @return the name of the field accessed by {@code tree}. */ public static String getFieldName(Tree tree) { assert isFieldAccess(tree); @@ -758,45 +818,39 @@ public final class TreeUtils { } /** - * Determine whether <code>tree</code> refers to a method element, such - * as + * Determine whether {@code tree} refers to a method element, such as * * <pre> * <em>m</em>(...) * <em>obj</em> . <em>m</em>(...) * </pre> * - * @return true iff if tree is a method access expression (implicit or - * explicit). + * @return true iff if tree is a method access expression (implicit or explicit) */ public static boolean isMethodAccess(Tree tree) { if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) { // explicit method access MemberSelectTree memberSelect = (MemberSelectTree) tree; Element el = TreeUtils.elementFromUse(memberSelect); - return el.getKind() == ElementKind.METHOD - || el.getKind() == ElementKind.CONSTRUCTOR; + return el.getKind() == ElementKind.METHOD || el.getKind() == ElementKind.CONSTRUCTOR; } else if (tree.getKind().equals(Tree.Kind.IDENTIFIER)) { // implicit method access IdentifierTree ident = (IdentifierTree) tree; // The field "super" and "this" are also legal methods - if (ident.getName().contentEquals("super") - || ident.getName().contentEquals("this")) { + if (ident.getName().contentEquals("super") || ident.getName().contentEquals("this")) { return true; } Element el = TreeUtils.elementFromUse(ident); - return el.getKind() == ElementKind.METHOD - || el.getKind() == ElementKind.CONSTRUCTOR; + return el.getKind() == ElementKind.METHOD || el.getKind() == ElementKind.CONSTRUCTOR; } return false; } /** - * Compute the name of the method that the method access <code>tree</code> - * accesses. Requires <code>tree</code> to be a method access, as determined - * by <code>isMethodAccess</code>. + * Compute the name of the method that the method access {@code tree} accesses. Requires {@code + * tree} to be a method access, as determined by {@code isMethodAccess}. * - * @return The name of the method accessed by <code>tree</code>. + * @return the name of the method accessed by {@code tree}. */ public static String getMethodName(Tree tree) { assert isMethodAccess(tree); @@ -810,19 +864,17 @@ public final class TreeUtils { } /** - * @return {@code true} if and only if {@code tree} can have a type - * annotation. - * - * TODO: is this implementation precise enough? E.g. does - * a .class literal work correctly? + * @return {@code true} if and only if {@code tree} can have a type annotation. + * <p>TODO: is this implementation precise enough? E.g. does a .class literal work + * correctly? */ public static boolean canHaveTypeAnnotation(Tree tree) { return ((JCTree) tree).type != null; } /** - * Returns true if and only if the given {@code tree} represents a field - * access of the given {@link VariableElement}. + * Returns true if and only if the given {@code tree} represents a field access of the given + * {@link VariableElement}. */ public static boolean isSpecificFieldAccess(Tree tree, VariableElement var) { if (tree instanceof MemberSelectTree) { @@ -841,12 +893,13 @@ public final class TreeUtils { /** * Returns the VariableElement for a field declaration. * - * @param typeName the class where the field is declared. - * @param fieldName the name of the field. - * @param env the processing environment. + * @param typeName the class where the field is declared + * @param fieldName the name of the field + * @param env the processing environment * @return the VariableElement for typeName.fieldName */ - public static VariableElement getField(String typeName, String fieldName, ProcessingEnvironment env) { + public static VariableElement getField( + String typeName, String fieldName, ProcessingEnvironment env) { TypeElement mapElt = env.getElementUtils().getTypeElement(typeName); for (VariableElement var : ElementFilter.fieldsIn(mapElt.getEnclosedElements())) { if (var.getSimpleName().contentEquals(fieldName)) { @@ -857,11 +910,12 @@ public final class TreeUtils { return null; // dead code } - /** Determine whether the given tree represents an ExpressionTree. + /** + * Determine whether the given tree represents an ExpressionTree. * - * TODO: is there a nicer way than an instanceof? + * <p>TODO: is there a nicer way than an instanceof? * - * @param tree the Tree to test. + * @param tree the Tree to test * @return whether the tree is an ExpressionTree */ public static boolean isExpressionTree(Tree tree) { @@ -880,16 +934,17 @@ public final class TreeUtils { return correctClass && correctMethod; } - /** Determine whether the given tree represents a declaration of a type - * (including type parameters). + /** + * Determine whether the given tree represents a declaration of a type (including type + * parameters). * - * @param node the Tree to test + * @param node the Tree to test * @return true if the tree is a type declaration */ public static boolean isTypeDeclaration(Tree node) { switch (node.getKind()) { - // These tree kinds are always declarations. Uses of the declared - // types have tree kind IDENTIFIER. + // These tree kinds are always declarations. Uses of the declared + // types have tree kind IDENTIFIER. case ANNOTATION_TYPE: case CLASS: case ENUM: @@ -901,4 +956,65 @@ public final class TreeUtils { return false; } } + + /** + * @see Object#getClass() + * @return true iff invocationTree is an instance of getClass() + */ + public static boolean isGetClassInvocation(MethodInvocationTree invocationTree) { + final Element declarationElement = elementFromUse(invocationTree); + String ownerName = + ElementUtils.getQualifiedClassName(declarationElement.getEnclosingElement()) + .toString(); + return ownerName.equals("java.lang.Object") + && declarationElement.getSimpleName().toString().equals("getClass"); + } + + /** + * Returns whether or not the leaf of the tree path is in a static scope. + * + * @param path TreePath whose leaf may or may not be in static scope + * @return returns whether or not the leaf of the tree path is in a static scope + */ + public static boolean isTreeInStaticScope(TreePath path) { + MethodTree enclosingMethod = TreeUtils.enclosingMethod(path); + + if (enclosingMethod != null) { + return enclosingMethod.getModifiers().getFlags().contains(Modifier.STATIC); + } + // no enclosing method, check for static or initializer block + BlockTree block = enclosingTopLevelBlock(path); + if (block != null) { + return block.isStatic(); + } + + // check if its in a variable initializer + Tree t = enclosingVariable(path); + if (t != null) { + return ((VariableTree) t).getModifiers().getFlags().contains((Modifier.STATIC)); + } + ClassTree classTree = enclosingClass(path); + if (classTree != null) { + return classTree.getModifiers().getFlags().contains((Modifier.STATIC)); + } + return false; + } + + /** + * Returns whether or not tree is an access of array length. + * + * @param tree tree to check + * @return true if tree is an access of array length + */ + public static boolean isArrayLengthAccess(Tree tree) { + if (tree.getKind() == Kind.MEMBER_SELECT + && isFieldAccess(tree) + && getFieldName(tree).equals("length")) { + ExpressionTree expressionTree = ((MemberSelectTree) tree).getExpression(); + if (InternalUtils.typeOf(expressionTree).getKind() == TypeKind.ARRAY) { + return true; + } + } + return false; + } } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypeAnnotationUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypeAnnotationUtils.java index c166e43f47..b925f91b04 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypeAnnotationUtils.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypeAnnotationUtils.java @@ -1,11 +1,20 @@ package org.checkerframework.javacutil; -import java.lang.reflect.InvocationTargetException; +import com.sun.tools.javac.code.Attribute; +import com.sun.tools.javac.code.Attribute.TypeCompound; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.TargetType; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.TypeAnnotationPosition; +import com.sun.tools.javac.processing.JavacProcessingEnvironment; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Name; +import com.sun.tools.javac.util.Pair; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Map; - import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValueVisitor; @@ -18,18 +27,6 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; -import com.sun.tools.javac.code.Attribute; -import com.sun.tools.javac.code.Attribute.TypeCompound; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.code.TargetType; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.code.TypeAnnotationPosition; -import com.sun.tools.javac.processing.JavacProcessingEnvironment; -import com.sun.tools.javac.tree.JCTree.JCLambda; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.List; -import com.sun.tools.javac.util.Pair; - /** * A collection of helper methods related to type annotation handling. * @@ -38,111 +35,117 @@ import com.sun.tools.javac.util.Pair; public class TypeAnnotationUtils { // Class cannot be instantiated. - private TypeAnnotationUtils() { throw new AssertionError("Class TypeAnnotationUtils cannot be instantiated."); } + private TypeAnnotationUtils() { + throw new AssertionError("Class TypeAnnotationUtils cannot be instantiated."); + } /** * Check whether a TypeCompound is contained in a list of TypeCompounds. * - * @param list The input list of TypeCompounds. - * @param tc The TypeCompound to find. - * @return true, iff a TypeCompound equal to tc is contained in list. + * @param list the input list of TypeCompounds + * @param tc the TypeCompound to find + * @return true, iff a TypeCompound equal to tc is contained in list */ - public static boolean isTypeCompoundContained(Types types, List<TypeCompound> list, TypeCompound tc) { + public static boolean isTypeCompoundContained( + Types types, List<TypeCompound> list, TypeCompound tc) { for (Attribute.TypeCompound rawat : list) { - if (rawat.type.tsym.name.contentEquals(tc.type.tsym.name) && + if (contentEquals(rawat.type.tsym.name, tc.type.tsym.name) // TODO: in previous line, it would be nicer to use reference equality: // rawat.type == tc.type && // or at least "isSameType": // types.isSameType(rawat.type, tc.type) && // but each fails in some cases. - rawat.values.equals(tc.values) && - isSameTAPosition(rawat.position, tc.position)) { + && rawat.values.equals(tc.values) + && isSameTAPositionExceptTreePos(rawat.position, tc.position)) { return true; } } return false; } + private static boolean contentEquals(Name n1, Name n2) { + // Views of underlying bytes, not copies as with Name#contentEquals + ByteBuffer b1 = ByteBuffer.wrap(n1.getByteArray(), n1.getByteOffset(), n1.getByteLength()); + ByteBuffer b2 = ByteBuffer.wrap(n2.getByteArray(), n2.getByteOffset(), n2.getByteLength()); + + return b1.equals(b2); + } + /** * Compare two TypeAnnotationPositions for equality. * - * @param p1 The first position. - * @param p2 The second position. - * @return true, iff the two positions are equal. + * @param p1 the first position + * @param p2 the second position + * @return true, iff the two positions are equal */ - public static boolean isSameTAPosition(TypeAnnotationPosition p1, - TypeAnnotationPosition p2) { - if (p1.isValidOffset == p2.isValidOffset && - p1.bound_index == p2.bound_index && - p1.exception_index == p2.exception_index && - p1.location.equals(p2.location) && - Arrays.equals(p1.lvarIndex, p2.lvarIndex) && - Arrays.equals(p1.lvarLength, p2.lvarLength) && - Arrays.equals(p1.lvarOffset, p2.lvarOffset) && - p1.offset == p2.offset && - p1.onLambda == p2.onLambda && - p1.parameter_index == p2.parameter_index && - p1.pos == p2.pos && - p1.type == p2.type && - p1.type_index == p2.type_index) { - return true; - } - return false; + public static boolean isSameTAPosition(TypeAnnotationPosition p1, TypeAnnotationPosition p2) { + return isSameTAPositionExceptTreePos(p1, p2) && p1.pos == p2.pos; + } + + public static boolean isSameTAPositionExceptTreePos( + TypeAnnotationPosition p1, TypeAnnotationPosition p2) { + return p1.isValidOffset == p2.isValidOffset + && p1.bound_index == p2.bound_index + && p1.exception_index == p2.exception_index + && p1.location.equals(p2.location) + && Arrays.equals(p1.lvarIndex, p2.lvarIndex) + && Arrays.equals(p1.lvarLength, p2.lvarLength) + && Arrays.equals(p1.lvarOffset, p2.lvarOffset) + && p1.offset == p2.offset + && p1.onLambda == p2.onLambda + && p1.parameter_index == p2.parameter_index + && p1.type == p2.type + && p1.type_index == p2.type_index; } /** - * Returns a newly created Attribute.Compound corresponding to an - * argument AnnotationMirror. + * Returns a newly created Attribute.Compound corresponding to an argument AnnotationMirror. * - * @param am an AnnotationMirror, which may be part of an AST or an internally - * created subclass. - * @return a new Attribute.Compound corresponding to the AnnotationMirror + * @param am an AnnotationMirror, which may be part of an AST or an internally created subclass + * @return a new Attribute.Compound corresponding to the AnnotationMirror */ - public static Attribute.Compound createCompoundFromAnnotationMirror(ProcessingEnvironment env, - AnnotationMirror am) { + public static Attribute.Compound createCompoundFromAnnotationMirror( + ProcessingEnvironment env, AnnotationMirror am) { // Create a new Attribute to match the AnnotationMirror. List<Pair<Symbol.MethodSymbol, Attribute>> values = List.nil(); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : - am.getElementValues().entrySet()) { - Attribute attribute = attributeFromAnnotationValue(env, entry.getKey(), entry.getValue()); - values = values.append(new Pair<>((Symbol.MethodSymbol)entry.getKey(), - attribute)); + am.getElementValues().entrySet()) { + Attribute attribute = + attributeFromAnnotationValue(env, entry.getKey(), entry.getValue()); + values = values.append(new Pair<>((Symbol.MethodSymbol) entry.getKey(), attribute)); } - return new Attribute.Compound((Type.ClassType)am.getAnnotationType(), values); + return new Attribute.Compound((Type.ClassType) am.getAnnotationType(), values); } /** - * Returns a newly created Attribute.TypeCompound corresponding to an - * argument AnnotationMirror. + * Returns a newly created Attribute.TypeCompound corresponding to an argument AnnotationMirror. * - * @param am an AnnotationMirror, which may be part of an AST or an internally - * created subclass. - * @param tapos the type annotation position to use. - * @return a new Attribute.TypeCompound corresponding to the AnnotationMirror + * @param am an AnnotationMirror, which may be part of an AST or an internally created subclass + * @param tapos the type annotation position to use + * @return a new Attribute.TypeCompound corresponding to the AnnotationMirror */ - public static Attribute.TypeCompound createTypeCompoundFromAnnotationMirror(ProcessingEnvironment env, - AnnotationMirror am, TypeAnnotationPosition tapos) { + public static Attribute.TypeCompound createTypeCompoundFromAnnotationMirror( + ProcessingEnvironment env, AnnotationMirror am, TypeAnnotationPosition tapos) { // Create a new Attribute to match the AnnotationMirror. List<Pair<Symbol.MethodSymbol, Attribute>> values = List.nil(); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : - am.getElementValues().entrySet()) { - Attribute attribute = attributeFromAnnotationValue(env, entry.getKey(), entry.getValue()); - values = values.append(new Pair<>((Symbol.MethodSymbol)entry.getKey(), - attribute)); + am.getElementValues().entrySet()) { + Attribute attribute = + attributeFromAnnotationValue(env, entry.getKey(), entry.getValue()); + values = values.append(new Pair<>((Symbol.MethodSymbol) entry.getKey(), attribute)); } - return new Attribute.TypeCompound((Type.ClassType)am.getAnnotationType(), values, tapos); + return new Attribute.TypeCompound((Type.ClassType) am.getAnnotationType(), values, tapos); } /** - * Returns a newly created Attribute corresponding to an argument - * AnnotationValue. + * Returns a newly created Attribute corresponding to an argument AnnotationValue. * - * @param meth the ExecutableElement that is assigned the value, needed for empty arrays. - * @param av an AnnotationValue, which may be part of an AST or an internally - * created subclass. - * @return a new Attribute corresponding to the AnnotationValue + * @param meth the ExecutableElement that is assigned the value, needed for empty arrays + * @param av an AnnotationValue, which may be part of an AST or an internally created subclass + * @return a new Attribute corresponding to the AnnotationValue */ - public static Attribute attributeFromAnnotationValue(ProcessingEnvironment env, ExecutableElement meth, AnnotationValue av) { + public static Attribute attributeFromAnnotationValue( + ProcessingEnvironment env, ExecutableElement meth, AnnotationValue av) { return av.accept(new AttributeCreator(env, meth), null); } @@ -156,7 +159,7 @@ public class TypeAnnotationUtils { public AttributeCreator(ProcessingEnvironment env, ExecutableElement meth) { this.processingEnv = env; - Context context = ((JavacProcessingEnvironment)env).getContext(); + Context context = ((JavacProcessingEnvironment) env).getContext(); this.elements = env.getElementUtils(); this.modelTypes = env.getTypeUtils(); this.javacTypes = com.sun.tools.javac.code.Types.instance(context); @@ -183,55 +186,55 @@ public class TypeAnnotationUtils { @Override public Attribute visitByte(byte b, Void p) { TypeMirror byteType = modelTypes.getPrimitiveType(TypeKind.BYTE); - return new Attribute.Constant((Type)byteType, b); + return new Attribute.Constant((Type) byteType, b); } @Override public Attribute visitChar(char c, Void p) { TypeMirror charType = modelTypes.getPrimitiveType(TypeKind.CHAR); - return new Attribute.Constant((Type)charType, c); + return new Attribute.Constant((Type) charType, c); } @Override public Attribute visitDouble(double d, Void p) { TypeMirror doubleType = modelTypes.getPrimitiveType(TypeKind.DOUBLE); - return new Attribute.Constant((Type)doubleType, d); + return new Attribute.Constant((Type) doubleType, d); } @Override public Attribute visitFloat(float f, Void p) { TypeMirror floatType = modelTypes.getPrimitiveType(TypeKind.FLOAT); - return new Attribute.Constant((Type)floatType, f); + return new Attribute.Constant((Type) floatType, f); } @Override public Attribute visitInt(int i, Void p) { TypeMirror intType = modelTypes.getPrimitiveType(TypeKind.INT); - return new Attribute.Constant((Type)intType, i); + return new Attribute.Constant((Type) intType, i); } @Override public Attribute visitLong(long i, Void p) { TypeMirror longType = modelTypes.getPrimitiveType(TypeKind.LONG); - return new Attribute.Constant((Type)longType, i); + return new Attribute.Constant((Type) longType, i); } @Override public Attribute visitShort(short s, Void p) { TypeMirror shortType = modelTypes.getPrimitiveType(TypeKind.SHORT); - return new Attribute.Constant((Type)shortType, s); + return new Attribute.Constant((Type) shortType, s); } @Override public Attribute visitString(String s, Void p) { TypeMirror stringType = elements.getTypeElement("java.lang.String").asType(); - return new Attribute.Constant((Type)stringType, s); + return new Attribute.Constant((Type) stringType, s); } @Override public Attribute visitType(TypeMirror t, Void p) { if (t instanceof Type) { - return new Attribute.Class(javacTypes, (Type)t); + return new Attribute.Class(javacTypes, (Type) t); } assert false : "Unexpected type of TypeMirror: " + t.getClass(); @@ -264,7 +267,7 @@ public class TypeAnnotationUtils { valAttrs = valAttrs.append(av.accept(this, p)); } ArrayType arrayType = modelTypes.getArrayType(valAttrs.get(0).type); - return new Attribute.Array((Type)arrayType, valAttrs); + return new Attribute.Array((Type) arrayType, valAttrs); } else { return new Attribute.Array((Type) meth.getReturnType(), List.<Attribute>nil()); } @@ -277,338 +280,118 @@ public class TypeAnnotationUtils { } } - /** - * An interface to abstract a Java 8 and a Java 9 version of how - * to get a RET reference. - * These methods must then be implemented using reflection in order to - * compile in either setting. - * Note that we cannot use lambda for this as long as we want to - * support Java 7. - */ - interface Call8or9<RET> { - RET call8() throws Throwable; - RET call9() throws Throwable; - } - - /** - * Use the SourceVersion to decide whether to call the Java 8 or Java 9 version. - * Catch all exceptions and abort if one occurs - the reflection code should - * never break once fully debugged. - * - * @param ver The SourceVersion to decide what API to use. - * @param tc The TAPCall abstraction to encapsulate two methods. - * @return The created TypeAnnotationPosition. - */ - private static <RET> RET call8or9(Call8or9<RET> tc) { - try { - boolean hasNine; - try { - hasNine = SourceVersion.valueOf("RELEASE_9") != null; - } catch(IllegalArgumentException iae) { - hasNine = false; - } - if (hasNine) { - return tc.call9(); - } else { - boolean hasEight; - try { - hasEight = SourceVersion.valueOf("RELEASE_8") != null; - } catch(IllegalArgumentException iae) { - hasEight = false; - } - if (hasEight) { - return tc.call8(); - } else { - assert false : "Checker Framework needs a Java 8 or 9 javac."; - return null; - } - } - } catch (Throwable t) { - assert false : "Checker Framework internal error: " + t; - t.printStackTrace(); - return null; - } - } - public static TypeAnnotationPosition unknownTAPosition() { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException { - return TypeAnnotationPosition.class.newInstance(); - } - @Override - public TypeAnnotationPosition call9() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getField("unknown") - .get(null); - } - } - ); + return new TypeAnnotationPosition(); } public static TypeAnnotationPosition methodReturnTAPosition(final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_RETURN); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("methodReturn", int.class) - .invoke(null, pos); - } - } - ); + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.METHOD_RETURN; + tapos.pos = pos; + return tapos; } public static TypeAnnotationPosition methodReceiverTAPosition(final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_RECEIVER); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("methodReceiver", int.class) - .invoke(null, pos); - } - } - ); + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.METHOD_RECEIVER; + tapos.pos = pos; + return tapos; } public static TypeAnnotationPosition methodParameterTAPosition(final int pidx, final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_FORMAL_PARAMETER); - TypeAnnotationPosition.class.getField("parameter_index").set(tapos, pidx); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("methodParameter", int.class, int.class) - .invoke(null, pidx, pos); - } - } - ); + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.METHOD_FORMAL_PARAMETER; + tapos.parameter_index = pidx; + tapos.pos = pos; + return tapos; } public static TypeAnnotationPosition methodThrowsTAPosition(final int tidx, final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.THROWS); - TypeAnnotationPosition.class.getField("type_index").set(tapos, tidx); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("methodThrows", List.class, JCLambda.class, int.class, int.class) - .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tidx, pos); - } - } - ); + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.THROWS; + tapos.type_index = tidx; + tapos.pos = pos; + return tapos; } public static TypeAnnotationPosition fieldTAPosition(final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.FIELD); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("field", int.class) - .invoke(null, pos); - } - } - ); + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.FIELD; + tapos.pos = pos; + return tapos; } public static TypeAnnotationPosition classExtendsTAPosition(final int implidx, final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.CLASS_EXTENDS); - TypeAnnotationPosition.class.getField("type_index").set(tapos, implidx); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("classExtends", int.class, int.class) - .invoke(null, implidx, pos); - } - } - ); + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.CLASS_EXTENDS; + tapos.type_index = implidx; + tapos.pos = pos; + return tapos; } public static TypeAnnotationPosition typeParameterTAPosition(final int tpidx, final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.CLASS_TYPE_PARAMETER); - TypeAnnotationPosition.class.getField("parameter_index").set(tapos, tpidx); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("typeParameter", List.class, JCLambda.class, int.class, int.class) - .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tpidx, pos); - } - } - ); + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.CLASS_TYPE_PARAMETER; + tapos.parameter_index = tpidx; + tapos.pos = pos; + return tapos; } - public static TypeAnnotationPosition methodTypeParameterTAPosition(final int tpidx, final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_TYPE_PARAMETER); - TypeAnnotationPosition.class.getField("parameter_index").set(tapos, tpidx); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("methodTypeParameter", List.class, JCLambda.class, int.class, int.class) - .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tpidx, pos); - } - } - ); + public static TypeAnnotationPosition methodTypeParameterTAPosition( + final int tpidx, final int pos) { + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.METHOD_TYPE_PARAMETER; + tapos.parameter_index = tpidx; + tapos.pos = pos; + return tapos; } - public static TypeAnnotationPosition typeParameterBoundTAPosition(final int tpidx, final int bndidx, final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.CLASS_TYPE_PARAMETER_BOUND); - TypeAnnotationPosition.class.getField("parameter_index").set(tapos, tpidx); - TypeAnnotationPosition.class.getField("bound_index").set(tapos, bndidx); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("typeParameterBound", List.class, JCLambda.class, int.class, int.class, int.class) - .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tpidx, bndidx, pos); - } - } - ); + public static TypeAnnotationPosition typeParameterBoundTAPosition( + final int tpidx, final int bndidx, final int pos) { + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.CLASS_TYPE_PARAMETER_BOUND; + tapos.parameter_index = tpidx; + tapos.bound_index = bndidx; + tapos.pos = pos; + return tapos; } - public static TypeAnnotationPosition methodTypeParameterBoundTAPosition(final int tpidx, final int bndidx, final int pos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance(); - TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_TYPE_PARAMETER_BOUND); - TypeAnnotationPosition.class.getField("parameter_index").set(tapos, tpidx); - TypeAnnotationPosition.class.getField("bound_index").set(tapos, bndidx); - TypeAnnotationPosition.class.getField("pos").set(tapos, pos); - return tapos; - } - @Override - public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("methodTypeParameterBound", List.class, JCLambda.class, int.class, int.class, int.class) - .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tpidx, bndidx, pos); - } - } - ); + public static TypeAnnotationPosition methodTypeParameterBoundTAPosition( + final int tpidx, final int bndidx, final int pos) { + TypeAnnotationPosition tapos = new TypeAnnotationPosition(); + tapos.type = TargetType.METHOD_TYPE_PARAMETER_BOUND; + tapos.parameter_index = tpidx; + tapos.bound_index = bndidx; + tapos.pos = pos; + return tapos; } public static TypeAnnotationPosition copyTAPosition(final TypeAnnotationPosition tapos) { - return call8or9( - new Call8or9<TypeAnnotationPosition>() { - @Override - public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - return copyTAPosition8(tapos); - } - @Override - public TypeAnnotationPosition call9() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, InvocationTargetException, NoSuchMethodException { - return (TypeAnnotationPosition) TypeAnnotationPosition.class - .getMethod("copy", TypeAnnotationPosition.class) - .invoke(null, tapos); - } - } - ); - } - - private static TypeAnnotationPosition copyTAPosition8(TypeAnnotationPosition tapos) throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException { - TypeAnnotationPosition res = TypeAnnotationPosition.class.newInstance(); + TypeAnnotationPosition res = new TypeAnnotationPosition(); res.isValidOffset = tapos.isValidOffset; - TypeAnnotationPosition.class.getField("bound_index").set(res, tapos.bound_index); + res.bound_index = tapos.bound_index; res.exception_index = tapos.exception_index; res.location = List.from(tapos.location); - if (tapos.lvarIndex != null) + if (tapos.lvarIndex != null) { res.lvarIndex = Arrays.copyOf(tapos.lvarIndex, tapos.lvarIndex.length); - if (tapos.lvarLength != null) + } + if (tapos.lvarLength != null) { res.lvarLength = Arrays.copyOf(tapos.lvarLength, tapos.lvarLength.length); - if (tapos.lvarOffset != null) + } + if (tapos.lvarOffset != null) { res.lvarOffset = Arrays.copyOf(tapos.lvarOffset, tapos.lvarOffset.length); + } res.offset = tapos.offset; - TypeAnnotationPosition.class.getField("onLambda").set(res, tapos.onLambda); - TypeAnnotationPosition.class.getField("parameter_index").set(res, tapos.parameter_index); - TypeAnnotationPosition.class.getField("pos").set(res, tapos.pos); - TypeAnnotationPosition.class.getField("type").set(res, tapos.type); - TypeAnnotationPosition.class.getField("type_index").set(res, tapos.type_index); + res.onLambda = tapos.onLambda; + res.parameter_index = tapos.parameter_index; + res.pos = tapos.pos; + res.type = tapos.type; + res.type_index = tapos.type_index; return res; } - public static Type unannotatedType(final Type in) { - return call8or9( - new Call8or9<Type>() { - @Override - public Type call8() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - return (Type) Type.class - .getMethod("unannotatedType") - .invoke(in); - } - @Override - public Type call9() { - return in; - } - } - ); + public static Type unannotatedType(final TypeMirror in) { + final Type impl = (Type) in; + return impl.unannotatedType(); } - -}
\ No newline at end of file +} diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypesUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypesUtils.java index f024a60feb..fe7456f6d2 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypesUtils.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypesUtils.java @@ -1,8 +1,19 @@ package org.checkerframework.javacutil; +import static com.sun.tools.javac.code.TypeTag.WILDCARD; + +import com.sun.tools.javac.code.Symtab; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.TypeTag; +import com.sun.tools.javac.model.JavacTypes; +import com.sun.tools.javac.processing.JavacProcessingEnvironment; +import com.sun.tools.javac.util.Context; import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; import javax.lang.model.element.Name; +import javax.lang.model.element.NestingKind; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; @@ -12,26 +23,18 @@ import javax.lang.model.type.WildcardType; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; -import com.sun.tools.javac.code.Symtab; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.code.TypeTag; -import com.sun.tools.javac.model.JavacTypes; -import com.sun.tools.javac.processing.JavacProcessingEnvironment; -import com.sun.tools.javac.util.Context; - -/** - * A utility class that helps with {@link TypeMirror}s. - * - */ +/** A utility class that helps with {@link TypeMirror}s. */ // TODO: This class needs significant restructuring public final class TypesUtils { // Class cannot be instantiated - private TypesUtils() { throw new AssertionError("Class TypesUtils cannot be instantiated."); } + private TypesUtils() { + throw new AssertionError("Class TypesUtils cannot be instantiated."); + } /** - * Gets the fully qualified name for a provided type. It returns an empty - * name if type is an anonymous type. + * Gets the fully qualified name for a provided type. It returns an empty name if type is an + * anonymous type. * * @param type the declared type * @return the name corresponding to that type @@ -44,7 +47,7 @@ public final class TypesUtils { /** * Checks if the type represents a java.lang.Object declared type. * - * @param type the type + * @param type the type * @return true iff type represents java.lang.Object */ public static boolean isObject(TypeMirror type) { @@ -54,7 +57,7 @@ public final class TypesUtils { /** * Checks if the type represents a java.lang.Class declared type. * - * @param type the type + * @param type the type * @return true iff type represents java.lang.Class */ public static boolean isClass(TypeMirror type) { @@ -62,11 +65,11 @@ public final class TypesUtils { } /** - * Checks if the type represents a java.lang.String declared type. - * TODO: it would be cleaner to use String.class.getCanonicalName(), but - * the two existing methods above don't do that, I guess for performance reasons. + * Checks if the type represents a java.lang.String declared type. TODO: it would be cleaner to + * use String.class.getCanonicalName(), but the two existing methods above don't do that, I + * guess for performance reasons. * - * @param type the type + * @param type the type * @return true iff type represents java.lang.String */ public static boolean isString(TypeMirror type) { @@ -74,8 +77,8 @@ public final class TypesUtils { } /** - * Checks if the type represents a boolean type, that is either boolean - * (primitive type) or java.lang.Boolean. + * Checks if the type represents a boolean type, that is either boolean (primitive type) or + * java.lang.Boolean. * * @param type the type to test * @return true iff type represents a boolean type @@ -93,14 +96,15 @@ public final class TypesUtils { */ public static boolean isDeclaredOfName(TypeMirror type, CharSequence qualifiedName) { return type.getKind() == TypeKind.DECLARED - && getQualifiedName((DeclaredType)type).contentEquals(qualifiedName); + && getQualifiedName((DeclaredType) type).contentEquals(qualifiedName); } public static boolean isBoxedPrimitive(TypeMirror type) { - if (type.getKind() != TypeKind.DECLARED) + if (type.getKind() != TypeKind.DECLARED) { return false; + } - String qualifiedName = getQualifiedName((DeclaredType)type).toString(); + String qualifiedName = getQualifiedName((DeclaredType) type).toString(); return (qualifiedName.equals("java.lang.Boolean") || qualifiedName.equals("java.lang.Byte") @@ -112,44 +116,57 @@ public final class TypesUtils { || qualifiedName.equals("java.lang.Float")); } - /** @return type represents a Throwable type (e.g. Exception, Error) **/ + /** @return type represents a Throwable type (e.g. Exception, Error) */ public static boolean isThrowable(TypeMirror type) { while (type != null && type.getKind() == TypeKind.DECLARED) { DeclaredType dt = (DeclaredType) type; TypeElement elem = (TypeElement) dt.asElement(); Name name = elem.getQualifiedName(); - if ("java.lang.Throwable".contentEquals(name)) + if ("java.lang.Throwable".contentEquals(name)) { return true; + } type = elem.getSuperclass(); } return false; } /** + * Returns true iff the argument is an anonymous type. + * + * @return whether the argument is an anonymous type + */ + public static boolean isAnonymous(TypeMirror type) { + return (type instanceof DeclaredType) + && (((TypeElement) ((DeclaredType) type).asElement()) + .getNestingKind() + .equals(NestingKind.ANONYMOUS)); + } + + /** * Returns true iff the argument is a primitive type. * - * @return whether the argument is a primitive type + * @return whether the argument is a primitive type */ public static boolean isPrimitive(TypeMirror type) { switch (type.getKind()) { - case BOOLEAN: - case BYTE: - case CHAR: - case DOUBLE: - case FLOAT: - case INT: - case LONG: - case SHORT: - return true; - default: - return false; + case BOOLEAN: + case BYTE: + case CHAR: + case DOUBLE: + case FLOAT: + case INT: + case LONG: + case SHORT: + return true; + default: + return false; } } /** * Returns true iff the arguments are both the same primitive types. * - * @return whether the arguments are the same primitive types + * @return whether the arguments are the same primitive types */ public static boolean areSamePrimitiveTypes(TypeMirror left, TypeMirror right) { if (!isPrimitive(left) || !isPrimitive(right)) { @@ -162,65 +179,64 @@ public final class TypesUtils { /** * Returns true iff the argument is a primitive numeric type. * - * @return whether the argument is a primitive numeric type + * @return whether the argument is a primitive numeric type */ public static boolean isNumeric(TypeMirror type) { switch (type.getKind()) { - case BYTE: - case CHAR: - case DOUBLE: - case FLOAT: - case INT: - case LONG: - case SHORT: - return true; - default: - return false; + case BYTE: + case CHAR: + case DOUBLE: + case FLOAT: + case INT: + case LONG: + case SHORT: + return true; + default: + return false; } } /** * Returns true iff the argument is an integral type. * - * @return whether the argument is an integral type + * @return whether the argument is an integral type */ public static boolean isIntegral(TypeMirror type) { switch (type.getKind()) { - case BYTE: - case CHAR: - case INT: - case LONG: - case SHORT: - return true; - default: - return false; + case BYTE: + case CHAR: + case INT: + case LONG: + case SHORT: + return true; + default: + return false; } } /** * Returns true iff the argument is a floating point type. * - * @return whether the argument is a floating point type + * @return whether the argument is a floating point type */ public static boolean isFloating(TypeMirror type) { switch (type.getKind()) { - case DOUBLE: - case FLOAT: - return true; - default: - return false; + case DOUBLE: + case FLOAT: + return true; + default: + return false; } } /** - * Returns the widened numeric type for an arithmetic operation - * performed on a value of the left type and the right type. - * Defined in JLS 5.6.2. We return a {@link TypeKind} because - * creating a {@link TypeMirror} requires a {@link Types} object - * from the {@link javax.annotation.processing.ProcessingEnvironment}. + * Returns the widened numeric type for an arithmetic operation performed on a value of the left + * type and the right type. Defined in JLS 5.6.2. We return a {@link TypeKind} because creating + * a {@link TypeMirror} requires a {@link Types} object from the {@link + * javax.annotation.processing.ProcessingEnvironment}. * - * @return the result of widening numeric conversion, or NONE when - * the conversion cannot be performed + * @return the result of widening numeric conversion, or NONE when the conversion cannot be + * performed */ public static TypeKind widenedNumericType(TypeMirror left, TypeMirror right) { if (!isNumeric(left) || !isNumeric(right)) { @@ -246,13 +262,12 @@ public final class TypesUtils { } /** - * If the argument is a bounded TypeVariable or WildcardType, - * return its non-variable, non-wildcard upper bound. Otherwise, - * return the type itself. + * If the argument is a bounded TypeVariable or WildcardType, return its non-variable, + * non-wildcard upper bound. Otherwise, return the type itself. * - * @param type a type - * @return the non-variable, non-wildcard upper bound of a type, - * if it has one, or itself if it has no bounds + * @param type a type + * @return the non-variable, non-wildcard upper bound of a type, if it has one, or itself if it + * has no bounds */ public static TypeMirror upperBound(TypeMirror type) { do { @@ -277,8 +292,28 @@ public final class TypesUtils { return type; } - // Version of com.sun.tools.javac.code.Types.wildUpperBound(Type) - // that works with both jdk8 (called upperBound there) and jdk8u. + /** + * Get the type parameter for this wildcard from the underlying type's bound field This field is + * sometimes null, in that case this method will return null + * + * @return the TypeParameterElement the wildcard is an argument to + */ + public static TypeParameterElement wildcardToTypeParam(final Type.WildcardType wildcard) { + + final Element typeParamElement; + if (wildcard.bound != null) { + typeParamElement = wildcard.bound.asElement(); + } else { + typeParamElement = null; + } + + return (TypeParameterElement) typeParamElement; + } + + /** + * Version of com.sun.tools.javac.code.Types.wildUpperBound(Type) that works with both jdk8 + * (called upperBound there) and jdk8u. + */ // TODO: contrast to upperBound. public static Type wildUpperBound(ProcessingEnvironment env, TypeMirror tm) { Type t = (Type) tm; @@ -291,15 +326,27 @@ public final class TypesUtils { } else { return wildUpperBound(env, w.type); } - } - else { + } else { return TypeAnnotationUtils.unannotatedType(t); } } /** - * Returns the {@link TypeMirror} for a given {@link Class}. + * Version of com.sun.tools.javac.code.Types.wildLowerBound(Type) that works with both jdk8 + * (called upperBound there) and jdk8u. */ + public static Type wildLowerBound(ProcessingEnvironment env, TypeMirror tm) { + Type t = (Type) tm; + if (t.hasTag(WILDCARD)) { + Context context = ((JavacProcessingEnvironment) env).getContext(); + Symtab syms = Symtab.instance(context); + Type.WildcardType w = (Type.WildcardType) TypeAnnotationUtils.unannotatedType(t); + return w.isExtendsBound() ? syms.botType : wildLowerBound(env, w.type); + } else { + return TypeAnnotationUtils.unannotatedType(t); + } + } + /** Returns the {@link TypeMirror} for a given {@link Class}. */ public static TypeMirror typeFromClass(Types types, Elements elements, Class<?> clazz) { if (clazz == void.class) { return types.getNoType(TypeKind.VOID); @@ -320,11 +367,86 @@ public final class TypesUtils { } } - /** - * Returns an {@link ArrayType} with elements of type {@code componentType}. - */ + /** Returns an {@link ArrayType} with elements of type {@code componentType}. */ public static ArrayType createArrayType(Types types, TypeMirror componentType) { JavacTypes t = (JavacTypes) types; return t.getArrayType(componentType); } + + /** + * Returns true if declaredType is a Class that is used to box primitive type (e.g. + * declaredType=java.lang.Double and primitiveType=22.5d ) + */ + public static boolean isBoxOf(TypeMirror declaredType, TypeMirror primitiveType) { + if (declaredType.getKind() != TypeKind.DECLARED) { + return false; + } + + final String qualifiedName = getQualifiedName((DeclaredType) declaredType).toString(); + switch (primitiveType.getKind()) { + case BOOLEAN: + return qualifiedName.equals("java.lang.Boolean"); + case BYTE: + return qualifiedName.equals("java.lang.Byte"); + case CHAR: + return qualifiedName.equals("java.lang.Character"); + case DOUBLE: + return qualifiedName.equals("java.lang.Double"); + case FLOAT: + return qualifiedName.equals("java.lang.Float"); + case INT: + return qualifiedName.equals("java.lang.Integer"); + case LONG: + return qualifiedName.equals("java.lang.Long"); + case SHORT: + return qualifiedName.equals("java.lang.Short"); + + default: + return false; + } + } + + /** + * Given a bounded type (wildcard or typevar) get the concrete type of its upper bound. If the + * bounded type extends other bounded types, this method will iterate through their bounds until + * a class, interface, or intersection is found. + * + * @return a type that is not a wildcard or typevar, or null if this type is an unbounded + * wildcard + */ + public static TypeMirror findConcreteUpperBound(final TypeMirror boundedType) { + TypeMirror effectiveUpper = boundedType; + outerLoop: + while (true) { + switch (effectiveUpper.getKind()) { + case WILDCARD: + effectiveUpper = + ((javax.lang.model.type.WildcardType) effectiveUpper).getExtendsBound(); + if (effectiveUpper == null) { + return null; + } + break; + + case TYPEVAR: + effectiveUpper = ((TypeVariable) effectiveUpper).getUpperBound(); + break; + + default: + break outerLoop; + } + } + return effectiveUpper; + } + + /** + * Returns true if the erased type of subtype is a subtype of the erased type of supertype. + * + * @param types Types + * @param subtype possible subtype + * @param supertype possible supertype + * @return true if the erased type of subtype is a subtype of the erased type of supertype + */ + public static boolean isErasedSubtype(Types types, TypeMirror subtype, TypeMirror supertype) { + return types.isSubtype(types.erasure(subtype), types.erasure(supertype)); + } } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/dist/ManualTaglet.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/dist/ManualTaglet.java index ade653e13b..01f35485e5 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/dist/ManualTaglet.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/dist/ManualTaglet.java @@ -1,22 +1,20 @@ package org.checkerframework.javacutil.dist; -import java.util.Map; - import com.sun.javadoc.Tag; import com.sun.tools.doclets.Taglet; +import java.util.Map; /** * A taglet for processing the {@code @checker_framework.manual} javadoc block tag, which inserts * references to the Checker Framework manual into javadoc. * - * <p> - * - * The {@code @checker_framework.manual} tag is used as follows: + * <p>The {@code @checker_framework.manual} tag is used as follows: * * <ul> - * <li>{@code @checker_framework.manual #} expands to a top-level link to the Checker Framework manual - * <li>{@code @checker_framework.manual #anchor text} expands to a link with some text to a - * particular part of the manual + * <li>{@code @checker_framework.manual #} expands to a top-level link to the Checker Framework + * manual + * <li>{@code @checker_framework.manual #anchor text} expands to a link with some text to a + * particular part of the manual * </ul> */ public class ManualTaglet implements Taglet { @@ -65,8 +63,8 @@ public class ManualTaglet implements Taglet { * Formats a link, given an array of tokens. * * @param parts the array of tokens - * @return a link to the manual top-level if the array size is one, or a - * link to a part of the manual if it's larger than one + * @return a link to the manual top-level if the array size is one, or a link to a part of the + * manual if it's larger than one */ private String formatLink(String[] parts) { String anchor, text; @@ -78,21 +76,18 @@ public class ManualTaglet implements Taglet { text = parts[1]; } return String.format( - "<A HREF=\"http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html%s\">%s</A>", - anchor, text); + "<A HREF=\"https://checkerframework.org/manual/%s\">%s</A>", anchor, text); } /** - * Formats the {@code @checker_framework.manual} tag, prepending the tag header to the - * tag content. + * Formats the {@code @checker_framework.manual} tag, prepending the tag header to the tag + * content. * * @param text the tag content * @return the formatted tag */ private String formatHeader(String text) { - return String.format( - "<DT><B>See the Checker Framework Manual:</B><DD>%s<BR>", - text); + return String.format("<DT><B>See the Checker Framework Manual:</B><DD>%s<BR>", text); } @Override @@ -103,19 +98,21 @@ public class ManualTaglet implements Taglet { @Override public String toString(Tag[] tags) { - if (tags.length == 0) + if (tags.length == 0) { return ""; + } StringBuilder sb = new StringBuilder(); for (Tag t : tags) { String[] split = t.text().split(" ", 2); - if (t != tags[0]) + if (t != tags[0]) { sb.append(", "); + } sb.append(formatLink(split)); } return formatHeader(sb.toString()); } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) public static void register(Map tagletMap) { ManualTaglet tag = new ManualTaglet(); Taglet t = (Taglet) tagletMap.get(tag.getName()); diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/DetachedVarSymbol.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/DetachedVarSymbol.java index 0edf3944e3..43d79cc710 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/DetachedVarSymbol.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/DetachedVarSymbol.java @@ -10,35 +10,26 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ /** - * A DetachedVarSymbol represents a variable that is not part of any - * AST Tree. DetachedVarSymbols are created when desugaring source - * code constructs and they carry important type information, but some - * methods such as TreeInfo.declarationFor do not work on them. + * A DetachedVarSymbol represents a variable that is not part of any AST Tree. DetachedVarSymbols + * are created when desugaring source code constructs and they carry important type information, but + * some methods such as TreeInfo.declarationFor do not work on them. */ - public class DetachedVarSymbol extends Symbol.VarSymbol { protected /*@Nullable*/ VariableTree decl; - /** - * Construct a detached variable symbol, given its flags, name, - * type and owner. - */ + /** Construct a detached variable symbol, given its flags, name, type and owner. */ public DetachedVarSymbol(long flags, Name name, Type type, Symbol owner) { super(flags, name, type, owner); this.decl = null; } - /** - * Set the declaration tree for the variable. - */ + /** Set the declaration tree for the variable. */ public void setDeclaration(VariableTree decl) { this.decl = decl; } - /** - * Get the declaration tree for the variable. - */ + /** Get the declaration tree for the variable. */ public /*@Nullable*/ VariableTree getDeclaration() { return decl; } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeBuilder.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeBuilder.java index 2623f2f2e7..eab005dea8 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeBuilder.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeBuilder.java @@ -1,24 +1,5 @@ package org.checkerframework.javacutil.trees; -import java.util.List; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Name; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.ArrayType; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.ElementFilter; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; - -import org.checkerframework.javacutil.InternalUtils; -import org.checkerframework.javacutil.TypesUtils; - import com.sun.source.tree.ArrayAccessTree; import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.BinaryTree; @@ -40,12 +21,27 @@ import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Names; +import java.util.List; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import org.checkerframework.javacutil.InternalUtils; +import org.checkerframework.javacutil.TypesUtils; /** - * The TreeBuilder permits the creation of new AST Trees using the - * non-public Java compiler API TreeMaker. + * The TreeBuilder permits the creation of new AST Trees using the non-public Java compiler API + * TreeMaker. */ - public class TreeBuilder { protected final Elements elements; protected final Types modelTypes; @@ -57,7 +53,7 @@ public class TreeBuilder { public TreeBuilder(ProcessingEnvironment env) { this.env = env; - Context context = ((JavacProcessingEnvironment)env).getContext(); + Context context = ((JavacProcessingEnvironment) env).getContext(); elements = env.getElementUtils(); modelTypes = env.getTypeUtils(); javacTypes = com.sun.tools.javac.code.Types.instance(context); @@ -67,66 +63,63 @@ public class TreeBuilder { } /** - * Builds an AST Tree to access the iterator() method of some iterable - * expression. + * Builds an AST Tree to access the iterator() method of some iterable expression. * - * @param iterableExpr an expression whose type is a subtype of Iterable - * @return a MemberSelectTree that accesses the iterator() method of - * the expression + * @param iterableExpr an expression whose type is a subtype of Iterable + * @return a MemberSelectTree that accesses the iterator() method of the expression */ public MemberSelectTree buildIteratorMethodAccess(ExpressionTree iterableExpr) { DeclaredType exprType = - (DeclaredType)TypesUtils.upperBound(InternalUtils.typeOf(iterableExpr)); + (DeclaredType) TypesUtils.upperBound(InternalUtils.typeOf(iterableExpr)); assert exprType != null : "expression must be of declared type Iterable<>"; - TypeElement exprElement = (TypeElement)exprType.asElement(); + TypeElement exprElement = (TypeElement) exprType.asElement(); // Find the iterator() method of the iterable type Symbol.MethodSymbol iteratorMethod = null; for (ExecutableElement method : - ElementFilter.methodsIn(elements.getAllMembers(exprElement))) { + ElementFilter.methodsIn(elements.getAllMembers(exprElement))) { Name methodName = method.getSimpleName(); if (method.getParameters().size() == 0) { if (methodName.contentEquals("iterator")) { - iteratorMethod = (Symbol.MethodSymbol)method; + iteratorMethod = (Symbol.MethodSymbol) method; } } } assert iteratorMethod != null : "no iterator method declared for expression type"; - Type.MethodType methodType = (Type.MethodType)iteratorMethod.asType(); + Type.MethodType methodType = (Type.MethodType) iteratorMethod.asType(); Symbol.TypeSymbol methodClass = methodType.asElement(); - DeclaredType iteratorType = (DeclaredType)methodType.getReturnType(); + DeclaredType iteratorType = (DeclaredType) methodType.getReturnType(); TypeMirror elementType; if (iteratorType.getTypeArguments().size() > 0) { elementType = iteratorType.getTypeArguments().get(0); // Remove captured type from a wildcard. if (elementType instanceof Type.CapturedType) { - elementType = ((Type.CapturedType)elementType).wildcard; + elementType = ((Type.CapturedType) elementType).wildcard; } iteratorType = - modelTypes.getDeclaredType((TypeElement)modelTypes.asElement(iteratorType), - elementType); + modelTypes.getDeclaredType( + (TypeElement) modelTypes.asElement(iteratorType), elementType); } - // Replace the iterator method's generic return type with // the actual element type of the expression. Type.MethodType updatedMethodType = - new Type.MethodType(com.sun.tools.javac.util.List.<Type>nil(), - (Type)iteratorType, - com.sun.tools.javac.util.List.<Type>nil(), - methodClass); + new Type.MethodType( + com.sun.tools.javac.util.List.<Type>nil(), + (Type) iteratorType, + com.sun.tools.javac.util.List.<Type>nil(), + methodClass); JCTree.JCFieldAccess iteratorAccess = - (JCTree.JCFieldAccess) - maker.Select((JCTree.JCExpression)iterableExpr, - iteratorMethod); + (JCTree.JCFieldAccess) + maker.Select((JCTree.JCExpression) iterableExpr, iteratorMethod); iteratorAccess.setType(updatedMethodType); return iteratorAccess; @@ -135,26 +128,25 @@ public class TreeBuilder { /** * Builds an AST Tree to access the hasNext() method of an iterator. * - * @param iteratorExpr an expression whose type is a subtype of Iterator - * @return a MemberSelectTree that accesses the hasNext() method of - * the expression + * @param iteratorExpr an expression whose type is a subtype of Iterator + * @return a MemberSelectTree that accesses the hasNext() method of the expression */ public MemberSelectTree buildHasNextMethodAccess(ExpressionTree iteratorExpr) { - DeclaredType exprType = (DeclaredType)InternalUtils.typeOf(iteratorExpr); + DeclaredType exprType = (DeclaredType) InternalUtils.typeOf(iteratorExpr); assert exprType != null : "expression must be of declared type Iterator<>"; - TypeElement exprElement = (TypeElement)exprType.asElement(); + TypeElement exprElement = (TypeElement) exprType.asElement(); // Find the hasNext() method of the iterator type Symbol.MethodSymbol hasNextMethod = null; for (ExecutableElement method : - ElementFilter.methodsIn(elements.getAllMembers(exprElement))) { + ElementFilter.methodsIn(elements.getAllMembers(exprElement))) { Name methodName = method.getSimpleName(); if (method.getParameters().size() == 0) { if (methodName.contentEquals("hasNext")) { - hasNextMethod = (Symbol.MethodSymbol)method; + hasNextMethod = (Symbol.MethodSymbol) method; } } } @@ -162,9 +154,8 @@ public class TreeBuilder { assert hasNextMethod != null : "no hasNext method declared for expression type"; JCTree.JCFieldAccess hasNextAccess = - (JCTree.JCFieldAccess) - maker.Select((JCTree.JCExpression)iteratorExpr, - hasNextMethod); + (JCTree.JCFieldAccess) + maker.Select((JCTree.JCExpression) iteratorExpr, hasNextMethod); hasNextAccess.setType(hasNextMethod.asType()); return hasNextAccess; @@ -173,38 +164,37 @@ public class TreeBuilder { /** * Builds an AST Tree to access the next() method of an iterator. * - * @param iteratorExpr an expression whose type is a subtype of Iterator - * @return a MemberSelectTree that accesses the next() method of - * the expression + * @param iteratorExpr an expression whose type is a subtype of Iterator + * @return a MemberSelectTree that accesses the next() method of the expression */ public MemberSelectTree buildNextMethodAccess(ExpressionTree iteratorExpr) { - DeclaredType exprType = (DeclaredType)InternalUtils.typeOf(iteratorExpr); + DeclaredType exprType = (DeclaredType) InternalUtils.typeOf(iteratorExpr); assert exprType != null : "expression must be of declared type Iterator<>"; - TypeElement exprElement = (TypeElement)exprType.asElement(); + TypeElement exprElement = (TypeElement) exprType.asElement(); // Find the next() method of the iterator type Symbol.MethodSymbol nextMethod = null; for (ExecutableElement method : - ElementFilter.methodsIn(elements.getAllMembers(exprElement))) { + ElementFilter.methodsIn(elements.getAllMembers(exprElement))) { Name methodName = method.getSimpleName(); if (method.getParameters().size() == 0) { if (methodName.contentEquals("next")) { - nextMethod = (Symbol.MethodSymbol)method; + nextMethod = (Symbol.MethodSymbol) method; } } } assert nextMethod != null : "no next method declared for expression type"; - Type.MethodType methodType = (Type.MethodType)nextMethod.asType(); + Type.MethodType methodType = (Type.MethodType) nextMethod.asType(); Symbol.TypeSymbol methodClass = methodType.asElement(); Type elementType; if (exprType.getTypeArguments().size() > 0) { - elementType = (Type)exprType.getTypeArguments().get(0); + elementType = (Type) exprType.getTypeArguments().get(0); } else { elementType = symtab.objectType; } @@ -212,15 +202,14 @@ public class TreeBuilder { // Replace the next method's generic return type with // the actual element type of the expression. Type.MethodType updatedMethodType = - new Type.MethodType(com.sun.tools.javac.util.List.<Type>nil(), - elementType, - com.sun.tools.javac.util.List.<Type>nil(), - methodClass); + new Type.MethodType( + com.sun.tools.javac.util.List.<Type>nil(), + elementType, + com.sun.tools.javac.util.List.<Type>nil(), + methodClass); JCTree.JCFieldAccess nextAccess = - (JCTree.JCFieldAccess) - maker.Select((JCTree.JCExpression)iteratorExpr, - nextMethod); + (JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) iteratorExpr, nextMethod); nextAccess.setType(updatedMethodType); return nextAccess; @@ -229,82 +218,80 @@ public class TreeBuilder { /** * Builds an AST Tree to dereference the length field of an array * - * @param expression the array expression whose length is being accessed - * @return a MemberSelectTree to dereference the length of the array + * @param expression the array expression whose length is being accessed + * @return a MemberSelectTree to dereference the length of the array */ public MemberSelectTree buildArrayLengthAccess(ExpressionTree expression) { return (JCTree.JCFieldAccess) - maker.Select((JCTree.JCExpression)expression, symtab.lengthVar); + maker.Select((JCTree.JCExpression) expression, symtab.lengthVar); } /** * Builds an AST Tree to call a method designated by the argument expression. * - * @param methodExpr an expression denoting a method with no arguments - * @return a MethodInvocationTree to call the argument method + * @param methodExpr an expression denoting a method with no arguments + * @return a MethodInvocationTree to call the argument method */ public MethodInvocationTree buildMethodInvocation(ExpressionTree methodExpr) { - return maker.App((JCTree.JCExpression)methodExpr); + return maker.App((JCTree.JCExpression) methodExpr); } /** - * Builds an AST Tree to call a method designated by methodExpr, - * with one argument designated by argExpr. + * Builds an AST Tree to call a method designated by methodExpr, with one argument designated by + * argExpr. * - * @param methodExpr an expression denoting a method with one argument - * @param argExpr an expression denoting an argument to the method - * @return a MethodInvocationTree to call the argument method + * @param methodExpr an expression denoting a method with one argument + * @param argExpr an expression denoting an argument to the method + * @return a MethodInvocationTree to call the argument method */ - public MethodInvocationTree buildMethodInvocation(ExpressionTree methodExpr, - ExpressionTree argExpr) { - return maker.App((JCTree.JCExpression)methodExpr, - com.sun.tools.javac.util.List.of((JCTree.JCExpression)argExpr)); + public MethodInvocationTree buildMethodInvocation( + ExpressionTree methodExpr, ExpressionTree argExpr) { + return maker.App( + (JCTree.JCExpression) methodExpr, + com.sun.tools.javac.util.List.of((JCTree.JCExpression) argExpr)); } /** * Builds an AST Tree to declare and initialize a variable, with no modifiers. * - * @param type the type of the variable - * @param name the name of the variable - * @param owner the element containing the new symbol - * @param initializer the initializer expression - * @return a VariableDeclTree declaring the new variable + * @param type the type of the variable + * @param name the name of the variable + * @param owner the element containing the new symbol + * @param initializer the initializer expression + * @return a VariableDeclTree declaring the new variable */ - public VariableTree buildVariableDecl(TypeMirror type, - String name, - Element owner, - ExpressionTree initializer) { + public VariableTree buildVariableDecl( + TypeMirror type, String name, Element owner, ExpressionTree initializer) { DetachedVarSymbol sym = - new DetachedVarSymbol(0, names.fromString(name), - (Type)type, (Symbol)owner); - VariableTree tree = maker.VarDef(sym, (JCTree.JCExpression)initializer); + new DetachedVarSymbol(0, names.fromString(name), (Type) type, (Symbol) owner); + VariableTree tree = maker.VarDef(sym, (JCTree.JCExpression) initializer); sym.setDeclaration(tree); return tree; } /** - * Builds an AST Tree to declare and initialize a variable. The - * type of the variable is specified by a Tree. + * Builds an AST Tree to declare and initialize a variable. The type of the variable is + * specified by a Tree. * - * @param type the type of the variable, as a Tree - * @param name the name of the variable - * @param owner the element containing the new symbol - * @param initializer the initializer expression - * @return a VariableDeclTree declaring the new variable + * @param type the type of the variable, as a Tree + * @param name the name of the variable + * @param owner the element containing the new symbol + * @param initializer the initializer expression + * @return a VariableDeclTree declaring the new variable */ - public VariableTree buildVariableDecl(Tree type, - String name, - Element owner, - ExpressionTree initializer) { - Type typeMirror = (Type)InternalUtils.typeOf(type); + public VariableTree buildVariableDecl( + Tree type, String name, Element owner, ExpressionTree initializer) { + Type typeMirror = (Type) InternalUtils.typeOf(type); DetachedVarSymbol sym = - new DetachedVarSymbol(0, names.fromString(name), - typeMirror, (Symbol)owner); + new DetachedVarSymbol(0, names.fromString(name), typeMirror, (Symbol) owner); JCTree.JCModifiers mods = maker.Modifiers(0); - JCTree.JCVariableDecl decl = maker.VarDef(mods, sym.name, - (JCTree.JCExpression)type, - (JCTree.JCExpression)initializer); + JCTree.JCVariableDecl decl = + maker.VarDef( + mods, + sym.name, + (JCTree.JCExpression) type, + (JCTree.JCExpression) initializer); decl.setType(typeMirror); decl.sym = sym; sym.setDeclaration(decl); @@ -314,57 +301,49 @@ public class TreeBuilder { /** * Builds an AST Tree to refer to a variable. * - * @param decl the declaration of the variable - * @return an IdentifierTree to refer to the variable + * @param decl the declaration of the variable + * @return an IdentifierTree to refer to the variable */ public IdentifierTree buildVariableUse(VariableTree decl) { - return (IdentifierTree)maker.Ident((JCTree.JCVariableDecl)decl); + return (IdentifierTree) maker.Ident((JCTree.JCVariableDecl) decl); } /** * Builds an AST Tree to cast the type of an expression. * - * @param type the type to cast to - * @param expr the expression to be cast - * @return a cast of the expression to the type + * @param type the type to cast to + * @param expr the expression to be cast + * @return a cast of the expression to the type */ - public TypeCastTree buildTypeCast(TypeMirror type, - ExpressionTree expr) { - return maker.TypeCast((Type)type, (JCTree.JCExpression)expr); + public TypeCastTree buildTypeCast(TypeMirror type, ExpressionTree expr) { + return maker.TypeCast((Type) type, (JCTree.JCExpression) expr); } /** * Builds an AST Tree to assign an expression to a variable. * - * @param variable the declaration of the variable to assign to - * @param expr the expression to be assigned - * @return a statement assigning the expression to the variable + * @param variable the declaration of the variable to assign to + * @param expr the expression to be assigned + * @return a statement assigning the expression to the variable */ - public StatementTree buildAssignment(VariableTree variable, - ExpressionTree expr) { - return maker.Assignment(TreeInfo.symbolFor((JCTree)variable), - (JCTree.JCExpression)expr); + public StatementTree buildAssignment(VariableTree variable, ExpressionTree expr) { + return maker.Assignment(TreeInfo.symbolFor((JCTree) variable), (JCTree.JCExpression) expr); } /** * Builds an AST Tree to assign an RHS expression to an LHS expression. * - * @param lhs the expression to be assigned to - * @param rhs the expression to be assigned - * @return a statement assigning the expression to the variable + * @param lhs the expression to be assigned to + * @param rhs the expression to be assigned + * @return a statement assigning the expression to the variable */ - public AssignmentTree buildAssignment(ExpressionTree lhs, - ExpressionTree rhs) { - JCTree.JCAssign assign = - maker.Assign((JCTree.JCExpression)lhs, (JCTree.JCExpression)rhs); - assign.setType((Type)InternalUtils.typeOf(lhs)); + public AssignmentTree buildAssignment(ExpressionTree lhs, ExpressionTree rhs) { + JCTree.JCAssign assign = maker.Assign((JCTree.JCExpression) lhs, (JCTree.JCExpression) rhs); + assign.setType((Type) InternalUtils.typeOf(lhs)); return assign; } - /** - * Builds an AST Tree representing a literal value of primitive - * or String type. - */ + /** Builds an AST Tree representing a literal value of primitive or String type. */ public LiteralTree buildLiteral(Object value) { return maker.Literal(value); } @@ -372,51 +351,48 @@ public class TreeBuilder { /** * Builds an AST Tree to compare two operands with less than. * - * @param left the left operand tree - * @param right the right operand tree - * @return a Tree representing "left < right" + * @param left the left operand tree + * @param right the right operand tree + * @return a Tree representing "left < right" */ public BinaryTree buildLessThan(ExpressionTree left, ExpressionTree right) { JCTree.JCBinary binary = - maker.Binary(JCTree.Tag.LT, (JCTree.JCExpression)left, - (JCTree.JCExpression)right); - binary.setType((Type)modelTypes.getPrimitiveType(TypeKind.BOOLEAN)); + maker.Binary( + JCTree.Tag.LT, (JCTree.JCExpression) left, (JCTree.JCExpression) right); + binary.setType((Type) modelTypes.getPrimitiveType(TypeKind.BOOLEAN)); return binary; } /** * Builds an AST Tree to dereference an array. * - * @param array the array to dereference - * @param index the index at which to dereference - * @return a Tree representing the dereference + * @param array the array to dereference + * @param index the index at which to dereference + * @return a Tree representing the dereference */ - public ArrayAccessTree buildArrayAccess(ExpressionTree array, - ExpressionTree index) { - ArrayType arrayType = (ArrayType)InternalUtils.typeOf(array); + public ArrayAccessTree buildArrayAccess(ExpressionTree array, ExpressionTree index) { + ArrayType arrayType = (ArrayType) InternalUtils.typeOf(array); JCTree.JCArrayAccess access = - maker.Indexed((JCTree.JCExpression)array, (JCTree.JCExpression)index); - access.setType((Type)arrayType.getComponentType()); + maker.Indexed((JCTree.JCExpression) array, (JCTree.JCExpression) index); + access.setType((Type) arrayType.getComponentType()); return access; } /** * Builds an AST Tree to refer to a class name. * - * @param elt an element representing the class - * @return an IdentifierTree referring to the class + * @param elt an element representing the class + * @return an IdentifierTree referring to the class */ public IdentifierTree buildClassUse(Element elt) { - return maker.Ident((Symbol)elt); + return maker.Ident((Symbol) elt); } /** - * Builds an AST Tree to access the valueOf() method of boxed type - * such as Short or Float. + * Builds an AST Tree to access the valueOf() method of boxed type such as Short or Float. * - * @param expr an expression whose type is a boxed type - * @return a MemberSelectTree that accesses the valueOf() method of - * the expression + * @param expr an expression whose type is a boxed type + * @return a MemberSelectTree that accesses the valueOf() method of the expression */ public MemberSelectTree buildValueOfMethodAccess(Tree expr) { TypeMirror boxedType = InternalUtils.typeOf(expr); @@ -426,32 +402,31 @@ public class TreeBuilder { // Find the valueOf(unboxedType) method of the boxed type Symbol.MethodSymbol valueOfMethod = getValueOfMethod(env, boxedType); - Type.MethodType methodType = (Type.MethodType)valueOfMethod.asType(); + Type.MethodType methodType = (Type.MethodType) valueOfMethod.asType(); JCTree.JCFieldAccess valueOfAccess = - (JCTree.JCFieldAccess) - maker.Select((JCTree.JCExpression)expr, valueOfMethod); + (JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) expr, valueOfMethod); valueOfAccess.setType(methodType); return valueOfAccess; } - /** - * Returns the valueOf method of a boxed type such as Short or Float. - */ - public static Symbol.MethodSymbol getValueOfMethod(ProcessingEnvironment env, TypeMirror boxedType) { + /** Returns the valueOf method of a boxed type such as Short or Float. */ + public static Symbol.MethodSymbol getValueOfMethod( + ProcessingEnvironment env, TypeMirror boxedType) { Symbol.MethodSymbol valueOfMethod = null; TypeMirror unboxedType = env.getTypeUtils().unboxedType(boxedType); - TypeElement boxedElement = (TypeElement)((DeclaredType)boxedType).asElement(); + TypeElement boxedElement = (TypeElement) ((DeclaredType) boxedType).asElement(); for (ExecutableElement method : - ElementFilter.methodsIn(env.getElementUtils().getAllMembers(boxedElement))) { + ElementFilter.methodsIn(env.getElementUtils().getAllMembers(boxedElement))) { Name methodName = method.getSimpleName(); if (methodName.contentEquals("valueOf")) { List<? extends VariableElement> params = method.getParameters(); - if (params.size() == 1 && env.getTypeUtils().isSameType(params.get(0).asType(), unboxedType)) { - valueOfMethod = (Symbol.MethodSymbol)method; + if (params.size() == 1 + && env.getTypeUtils().isSameType(params.get(0).asType(), unboxedType)) { + valueOfMethod = (Symbol.MethodSymbol) method; } } } @@ -461,17 +436,15 @@ public class TreeBuilder { } /** - * Builds an AST Tree to access the *Value() method of a - * boxed type such as Short or Float, where * is the corresponding - * primitive type (i.e. shortValue or floatValue). + * Builds an AST Tree to access the *Value() method of a boxed type such as Short or Float, + * where * is the corresponding primitive type (i.e. shortValue or floatValue). * - * @param expr an expression whose type is a boxed type - * @return a MemberSelectTree that accesses the *Value() method of - * the expression + * @param expr an expression whose type is a boxed type + * @return a MemberSelectTree that accesses the *Value() method of the expression */ public MemberSelectTree buildPrimValueMethodAccess(Tree expr) { TypeMirror boxedType = InternalUtils.typeOf(expr); - TypeElement boxedElement = (TypeElement)((DeclaredType)boxedType).asElement(); + TypeElement boxedElement = (TypeElement) ((DeclaredType) boxedType).asElement(); assert TypesUtils.isBoxedPrimitive(boxedType); TypeMirror unboxedType = modelTypes.unboxedType(boxedType); @@ -481,207 +454,202 @@ public class TreeBuilder { Symbol.MethodSymbol primValueMethod = null; for (ExecutableElement method : - ElementFilter.methodsIn(elements.getAllMembers(boxedElement))) { + ElementFilter.methodsIn(elements.getAllMembers(boxedElement))) { Name methodName = method.getSimpleName(); - if (methodName.contentEquals(primValueName) && - method.getParameters().size() == 0) { - primValueMethod = (Symbol.MethodSymbol)method; + if (methodName.contentEquals(primValueName) && method.getParameters().size() == 0) { + primValueMethod = (Symbol.MethodSymbol) method; } } assert primValueMethod != null : "no *Value method declared for boxed type"; - Type.MethodType methodType = (Type.MethodType)primValueMethod.asType(); + Type.MethodType methodType = (Type.MethodType) primValueMethod.asType(); JCTree.JCFieldAccess primValueAccess = - (JCTree.JCFieldAccess) - maker.Select((JCTree.JCExpression)expr, primValueMethod); + (JCTree.JCFieldAccess) maker.Select((JCTree.JCExpression) expr, primValueMethod); primValueAccess.setType(methodType); return primValueAccess; } - /** - * Map public AST Tree.Kinds to internal javac JCTree.Tags. - */ + /** Map public AST Tree.Kinds to internal javac JCTree.Tags. */ public JCTree.Tag kindToTag(Tree.Kind kind) { switch (kind) { - case AND: - return JCTree.Tag.BITAND; - case AND_ASSIGNMENT: - return JCTree.Tag.BITAND_ASG; - case ANNOTATION: - return JCTree.Tag.ANNOTATION; - case ANNOTATION_TYPE: - return JCTree.Tag.TYPE_ANNOTATION; - case ARRAY_ACCESS: - return JCTree.Tag.INDEXED; - case ARRAY_TYPE: - return JCTree.Tag.TYPEARRAY; - case ASSERT: - return JCTree.Tag.ASSERT; - case ASSIGNMENT: - return JCTree.Tag.ASSIGN; - case BITWISE_COMPLEMENT: - return JCTree.Tag.COMPL; - case BLOCK: - return JCTree.Tag.BLOCK; - case BREAK: - return JCTree.Tag.BREAK; - case CASE: - return JCTree.Tag.CASE; - case CATCH: - return JCTree.Tag.CATCH; - case CLASS: - return JCTree.Tag.CLASSDEF; - case CONDITIONAL_AND: - return JCTree.Tag.AND; - case CONDITIONAL_EXPRESSION: - return JCTree.Tag.CONDEXPR; - case CONDITIONAL_OR: - return JCTree.Tag.OR; - case CONTINUE: - return JCTree.Tag.CONTINUE; - case DIVIDE: - return JCTree.Tag.DIV; - case DIVIDE_ASSIGNMENT: - return JCTree.Tag.DIV_ASG; - case DO_WHILE_LOOP: - return JCTree.Tag.DOLOOP; - case ENHANCED_FOR_LOOP: - return JCTree.Tag.FOREACHLOOP; - case EQUAL_TO: - return JCTree.Tag.EQ; - case EXPRESSION_STATEMENT: - return JCTree.Tag.EXEC; - case FOR_LOOP: - return JCTree.Tag.FORLOOP; - case GREATER_THAN: - return JCTree.Tag.GT; - case GREATER_THAN_EQUAL: - return JCTree.Tag.GE; - case IDENTIFIER: - return JCTree.Tag.IDENT; - case IF: - return JCTree.Tag.IF; - case IMPORT: - return JCTree.Tag.IMPORT; - case INSTANCE_OF: - return JCTree.Tag.TYPETEST; - case LABELED_STATEMENT: - return JCTree.Tag.LABELLED; - case LEFT_SHIFT: - return JCTree.Tag.SL; - case LEFT_SHIFT_ASSIGNMENT: - return JCTree.Tag.SL_ASG; - case LESS_THAN: - return JCTree.Tag.LT; - case LESS_THAN_EQUAL: - return JCTree.Tag.LE; - case LOGICAL_COMPLEMENT: - return JCTree.Tag.NOT; - case MEMBER_SELECT: - return JCTree.Tag.SELECT; - case METHOD: - return JCTree.Tag.METHODDEF; - case METHOD_INVOCATION: - return JCTree.Tag.APPLY; - case MINUS: - return JCTree.Tag.MINUS; - case MINUS_ASSIGNMENT: - return JCTree.Tag.MINUS_ASG; - case MODIFIERS: - return JCTree.Tag.MODIFIERS; - case MULTIPLY: - return JCTree.Tag.MUL; - case MULTIPLY_ASSIGNMENT: - return JCTree.Tag.MUL_ASG; - case NEW_ARRAY: - return JCTree.Tag.NEWARRAY; - case NEW_CLASS: - return JCTree.Tag.NEWCLASS; - case NOT_EQUAL_TO: - return JCTree.Tag.NE; - case OR: - return JCTree.Tag.BITOR; - case OR_ASSIGNMENT: - return JCTree.Tag.BITOR_ASG; - case PARENTHESIZED: - return JCTree.Tag.PARENS; - case PLUS: - return JCTree.Tag.PLUS; - case PLUS_ASSIGNMENT: - return JCTree.Tag.PLUS_ASG; - case POSTFIX_DECREMENT: - return JCTree.Tag.POSTDEC; - case POSTFIX_INCREMENT: - return JCTree.Tag.POSTINC; - case PREFIX_DECREMENT: - return JCTree.Tag.PREDEC; - case PREFIX_INCREMENT: - return JCTree.Tag.PREINC; - case REMAINDER: - return JCTree.Tag.MOD; - case REMAINDER_ASSIGNMENT: - return JCTree.Tag.MOD_ASG; - case RETURN: - return JCTree.Tag.RETURN; - case RIGHT_SHIFT: - return JCTree.Tag.SR; - case RIGHT_SHIFT_ASSIGNMENT: - return JCTree.Tag.SR_ASG; - case SWITCH: - return JCTree.Tag.SWITCH; - case SYNCHRONIZED: - return JCTree.Tag.SYNCHRONIZED; - case THROW: - return JCTree.Tag.THROW; - case TRY: - return JCTree.Tag.TRY; - case TYPE_CAST: - return JCTree.Tag.TYPECAST; - case TYPE_PARAMETER: - return JCTree.Tag.TYPEPARAMETER; - case UNARY_MINUS: - return JCTree.Tag.NEG; - case UNARY_PLUS: - return JCTree.Tag.POS; - case UNION_TYPE: - return JCTree.Tag.TYPEUNION; - case UNSIGNED_RIGHT_SHIFT: - return JCTree.Tag.USR; - case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: - return JCTree.Tag.USR_ASG; - case VARIABLE: - return JCTree.Tag.VARDEF; - case WHILE_LOOP: - return JCTree.Tag.WHILELOOP; - case XOR: - return JCTree.Tag.BITXOR; - case XOR_ASSIGNMENT: - return JCTree.Tag.BITXOR_ASG; - default: - return JCTree.Tag.NO_TAG; + case AND: + return JCTree.Tag.BITAND; + case AND_ASSIGNMENT: + return JCTree.Tag.BITAND_ASG; + case ANNOTATION: + return JCTree.Tag.ANNOTATION; + case ANNOTATION_TYPE: + return JCTree.Tag.TYPE_ANNOTATION; + case ARRAY_ACCESS: + return JCTree.Tag.INDEXED; + case ARRAY_TYPE: + return JCTree.Tag.TYPEARRAY; + case ASSERT: + return JCTree.Tag.ASSERT; + case ASSIGNMENT: + return JCTree.Tag.ASSIGN; + case BITWISE_COMPLEMENT: + return JCTree.Tag.COMPL; + case BLOCK: + return JCTree.Tag.BLOCK; + case BREAK: + return JCTree.Tag.BREAK; + case CASE: + return JCTree.Tag.CASE; + case CATCH: + return JCTree.Tag.CATCH; + case CLASS: + return JCTree.Tag.CLASSDEF; + case CONDITIONAL_AND: + return JCTree.Tag.AND; + case CONDITIONAL_EXPRESSION: + return JCTree.Tag.CONDEXPR; + case CONDITIONAL_OR: + return JCTree.Tag.OR; + case CONTINUE: + return JCTree.Tag.CONTINUE; + case DIVIDE: + return JCTree.Tag.DIV; + case DIVIDE_ASSIGNMENT: + return JCTree.Tag.DIV_ASG; + case DO_WHILE_LOOP: + return JCTree.Tag.DOLOOP; + case ENHANCED_FOR_LOOP: + return JCTree.Tag.FOREACHLOOP; + case EQUAL_TO: + return JCTree.Tag.EQ; + case EXPRESSION_STATEMENT: + return JCTree.Tag.EXEC; + case FOR_LOOP: + return JCTree.Tag.FORLOOP; + case GREATER_THAN: + return JCTree.Tag.GT; + case GREATER_THAN_EQUAL: + return JCTree.Tag.GE; + case IDENTIFIER: + return JCTree.Tag.IDENT; + case IF: + return JCTree.Tag.IF; + case IMPORT: + return JCTree.Tag.IMPORT; + case INSTANCE_OF: + return JCTree.Tag.TYPETEST; + case LABELED_STATEMENT: + return JCTree.Tag.LABELLED; + case LEFT_SHIFT: + return JCTree.Tag.SL; + case LEFT_SHIFT_ASSIGNMENT: + return JCTree.Tag.SL_ASG; + case LESS_THAN: + return JCTree.Tag.LT; + case LESS_THAN_EQUAL: + return JCTree.Tag.LE; + case LOGICAL_COMPLEMENT: + return JCTree.Tag.NOT; + case MEMBER_SELECT: + return JCTree.Tag.SELECT; + case METHOD: + return JCTree.Tag.METHODDEF; + case METHOD_INVOCATION: + return JCTree.Tag.APPLY; + case MINUS: + return JCTree.Tag.MINUS; + case MINUS_ASSIGNMENT: + return JCTree.Tag.MINUS_ASG; + case MODIFIERS: + return JCTree.Tag.MODIFIERS; + case MULTIPLY: + return JCTree.Tag.MUL; + case MULTIPLY_ASSIGNMENT: + return JCTree.Tag.MUL_ASG; + case NEW_ARRAY: + return JCTree.Tag.NEWARRAY; + case NEW_CLASS: + return JCTree.Tag.NEWCLASS; + case NOT_EQUAL_TO: + return JCTree.Tag.NE; + case OR: + return JCTree.Tag.BITOR; + case OR_ASSIGNMENT: + return JCTree.Tag.BITOR_ASG; + case PARENTHESIZED: + return JCTree.Tag.PARENS; + case PLUS: + return JCTree.Tag.PLUS; + case PLUS_ASSIGNMENT: + return JCTree.Tag.PLUS_ASG; + case POSTFIX_DECREMENT: + return JCTree.Tag.POSTDEC; + case POSTFIX_INCREMENT: + return JCTree.Tag.POSTINC; + case PREFIX_DECREMENT: + return JCTree.Tag.PREDEC; + case PREFIX_INCREMENT: + return JCTree.Tag.PREINC; + case REMAINDER: + return JCTree.Tag.MOD; + case REMAINDER_ASSIGNMENT: + return JCTree.Tag.MOD_ASG; + case RETURN: + return JCTree.Tag.RETURN; + case RIGHT_SHIFT: + return JCTree.Tag.SR; + case RIGHT_SHIFT_ASSIGNMENT: + return JCTree.Tag.SR_ASG; + case SWITCH: + return JCTree.Tag.SWITCH; + case SYNCHRONIZED: + return JCTree.Tag.SYNCHRONIZED; + case THROW: + return JCTree.Tag.THROW; + case TRY: + return JCTree.Tag.TRY; + case TYPE_CAST: + return JCTree.Tag.TYPECAST; + case TYPE_PARAMETER: + return JCTree.Tag.TYPEPARAMETER; + case UNARY_MINUS: + return JCTree.Tag.NEG; + case UNARY_PLUS: + return JCTree.Tag.POS; + case UNION_TYPE: + return JCTree.Tag.TYPEUNION; + case UNSIGNED_RIGHT_SHIFT: + return JCTree.Tag.USR; + case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: + return JCTree.Tag.USR_ASG; + case VARIABLE: + return JCTree.Tag.VARDEF; + case WHILE_LOOP: + return JCTree.Tag.WHILELOOP; + case XOR: + return JCTree.Tag.BITXOR; + case XOR_ASSIGNMENT: + return JCTree.Tag.BITXOR_ASG; + default: + return JCTree.Tag.NO_TAG; } } /** * Builds an AST Tree to perform a binary operation. * - * @param type result type of the operation - * @param op AST Tree operator - * @param left the left operand tree - * @param right the right operand tree - * @return a Tree representing "left < right" + * @param type result type of the operation + * @param op AST Tree operator + * @param left the left operand tree + * @param right the right operand tree + * @return a Tree representing "left < right" */ - public BinaryTree buildBinary(TypeMirror type, Tree.Kind op, ExpressionTree left, ExpressionTree right) { + public BinaryTree buildBinary( + TypeMirror type, Tree.Kind op, ExpressionTree left, ExpressionTree right) { JCTree.Tag jcOp = kindToTag(op); JCTree.JCBinary binary = - maker.Binary(jcOp, (JCTree.JCExpression)left, - (JCTree.JCExpression)right); - binary.setType((Type)type); + maker.Binary(jcOp, (JCTree.JCExpression) left, (JCTree.JCExpression) right); + binary.setType((Type) type); return binary; } - } diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java index ea7f67f69e..9ea545e55c 100644 --- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java +++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java @@ -1,9 +1,5 @@ package org.checkerframework.javacutil.trees; -import java.util.StringTokenizer; - -import javax.annotation.processing.ProcessingEnvironment; - import com.sun.source.tree.ExpressionTree; import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -12,27 +8,28 @@ import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Names; +import java.util.StringTokenizer; +import javax.annotation.processing.ProcessingEnvironment; /** - * A Utility class for parsing Java expression snippets, and converting them - * to proper Javac AST nodes. + * A Utility class for parsing Java expression snippets, and converting them to proper Javac AST + * nodes. + * + * <p>This is useful for parsing {@code EnsuresNonNull*}, and {@code KeyFor} values. * - * This is useful for parsing {@code EnsuresNonNull*}, - * and {@code KeyFor} values. + * <p>Currently, it handles four tree types only: * - * Currently, it handles four tree types only: * <ul> - * <li>Identifier tree (e.g. {@code id})</li> - * <li>Literal tree (e.g. 2, 3)</li> - * <li>Method invocation tree (e.g. {@code method(2, 3)})</li> - * <li>Member select tree (e.g. {@code Class.field}, {@code instance.method()}) - * <li>Array access tree (e.g. {@code array[id]})</li> + * <li>Identifier tree (e.g. {@code id}) + * <li>Literal tree (e.g. 2, 3) + * <li>Method invocation tree (e.g. {@code method(2, 3)}) + * <li>Member select tree (e.g. {@code Class.field}, {@code instance.method()}) + * <li>Array access tree (e.g. {@code array[id]}) * </ul> * - * Notable limitation: Doesn't handle spaces, or non-method-argument - * parenthesis. + * Notable limitation: Doesn't handle spaces, or non-method-argument parenthesis. * - * It's implemented via a Recursive-Descend parser. + * <p>It's implemented via a Recursive-Descend parser. */ public class TreeParser { private static final String DELIMS = ".[](),"; @@ -42,17 +39,16 @@ public class TreeParser { private final Names names; public TreeParser(ProcessingEnvironment env) { - Context context = ((JavacProcessingEnvironment)env).getContext(); + Context context = ((JavacProcessingEnvironment) env).getContext(); maker = TreeMaker.instance(context); names = Names.instance(context); } /** - * Parses the snippet in the string as an internal Javac AST expression - * node + * Parses the snippet in the string as an internal Javac AST expression node * * @param s the java snippet - * @return the AST corresponding to the snippet + * @return the AST corresponding to the snippet */ public ExpressionTree parseTree(String s) { tokenizer = new StringTokenizer(s, DELIMS, true); @@ -84,15 +80,19 @@ public class TreeParser { return maker.Literal(false); } - if (Character.isLetter(token.charAt(0))) + if (Character.isLetter(token.charAt(0))) { return maker.Ident(names.fromString(token)); + } Object value = null; try { value = Integer.valueOf(token); - } catch (Exception e2) { try { - value = Double.valueOf(token); - } catch (Exception ef) {}} + } catch (Exception e2) { + try { + value = Double.valueOf(token); + } catch (Exception ef) { + } + } assert value != null; return maker.Literal(value); } @@ -104,21 +104,20 @@ public class TreeParser { String delim = nextToken(); if (".".equals(delim)) { nextToken(); - tree = maker.Select(tree, - names.fromString(token)); + tree = maker.Select(tree, names.fromString(token)); } else if ("(".equals(delim)) { nextToken(); ListBuffer<JCExpression> args = new ListBuffer<>(); while (!")".equals(token)) { JCExpression arg = parseExpression(); args.append(arg); - if (",".equals(token)) + if (",".equals(token)) { nextToken(); + } } // For now, handle empty args only assert ")".equals(token); - tree = maker.Apply(List.<JCExpression>nil(), - tree, args.toList()); + tree = maker.Apply(List.<JCExpression>nil(), tree, args.toList()); } else if ("[".equals(token)) { nextToken(); JCExpression index = parseExpression(); @@ -132,7 +131,7 @@ public class TreeParser { return tree; } - class ParseError extends RuntimeException { + private static class ParseError extends RuntimeException { private static final long serialVersionUID = 1887754619522101929L; ParseError(Throwable cause) { |