| package org.checkerframework.common.util.debug; |
| |
| import com.sun.source.tree.ClassTree; |
| import com.sun.source.tree.MethodTree; |
| import com.sun.source.tree.VariableTree; |
| import com.sun.tools.javac.processing.JavacProcessingEnvironment; |
| import com.sun.tools.javac.util.Context; |
| import java.util.Collection; |
| import java.util.Set; |
| 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.element.VariableElement; |
| import javax.lang.model.util.Elements; |
| import org.checkerframework.checker.signature.qual.CanonicalName; |
| import org.checkerframework.common.basetype.BaseTypeChecker; |
| import org.checkerframework.common.basetype.BaseTypeVisitor; |
| import org.checkerframework.framework.type.AnnotatedTypeFactory; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; |
| import org.checkerframework.framework.type.GenericAnnotatedTypeFactory; |
| import org.checkerframework.framework.type.QualifierHierarchy; |
| import org.checkerframework.javacutil.AnnotationUtils; |
| import org.checkerframework.javacutil.BugInCF; |
| import org.checkerframework.javacutil.TreeUtils; |
| |
| /** |
| * A testing class that can be used to test {@link TypeElement}. In particular it tests that the |
| * types read from classfiles are the same to the ones from java files. |
| * |
| * <p>For testing, you need to do the following: |
| * |
| * <ol> |
| * <li>Run the Checker on the source file like any checker: |
| * <pre>{@code |
| * java -processor org.checkerframework.common.util.debug.TypeOutputtingChecker [source-file] |
| * |
| * }</pre> |
| * <li>Run the Checker on the bytecode, by simply running the main and passing the qualified name, |
| * e.g. |
| * <pre>{@code |
| * java org.checkerframework.common.util.debug.TypeOutputtingChecker [qualified-name] |
| * |
| * }</pre> |
| * <li>Apply a simple diff on the two outputs |
| * </ol> |
| */ |
| public class TypeOutputtingChecker extends BaseTypeChecker { |
| |
| @Override |
| protected BaseTypeVisitor<?> createSourceVisitor() { |
| return new Visitor(this); |
| } |
| |
| /** Prints the types of the class and all of its enclosing fields, methods, and inner classes. */ |
| public static class Visitor extends BaseTypeVisitor<GenericAnnotatedTypeFactory<?, ?, ?, ?>> { |
| String currentClass; |
| |
| public Visitor(BaseTypeChecker checker) { |
| super(checker); |
| } |
| |
| // Print types of classes, methods, and fields |
| @Override |
| public void processClassTree(ClassTree node) { |
| TypeElement element = TreeUtils.elementFromDeclaration(node); |
| currentClass = element.getSimpleName().toString(); |
| |
| AnnotatedDeclaredType type = atypeFactory.getAnnotatedType(node); |
| System.out.println(node.getSimpleName() + "\t" + type + "\t" + type.directSupertypes()); |
| |
| super.processClassTree(node); |
| } |
| |
| @Override |
| public Void visitMethod(MethodTree node, Void p) { |
| ExecutableElement elem = TreeUtils.elementFromDeclaration(node); |
| |
| AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(node); |
| System.out.println(currentClass + "." + elem + "\t\t" + type); |
| // Don't dig deeper |
| return null; |
| } |
| |
| @Override |
| public Void visitVariable(VariableTree node, Void p) { |
| VariableElement elem = TreeUtils.elementFromDeclaration(node); |
| if (elem.getKind().isField()) { |
| AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(node); |
| System.out.println(currentClass + "." + elem + "\t\t" + type); |
| } |
| |
| // Don't dig deeper |
| return null; |
| } |
| } |
| |
| /** |
| * Main entry point. |
| * |
| * @param args command-line arguments |
| */ |
| @SuppressWarnings("signature:argument") // user-supplied input, uncheckable |
| public static void main(String[] args) { |
| new TypeOutputtingChecker().run(args); |
| } |
| |
| /** |
| * Run the test. |
| * |
| * @param args command-line arguments |
| */ |
| public void run(@CanonicalName String[] args) { |
| ProcessingEnvironment env = JavacProcessingEnvironment.instance(new Context()); |
| Elements elements = env.getElementUtils(); |
| |
| // TODO: Instead of using a GeneralAnnotatedTypeFactory, just use standard javac classes |
| // to print explicit annotations. |
| AnnotatedTypeFactory atypeFactory = new GeneralAnnotatedTypeFactory(this); |
| |
| for (String className : args) { |
| TypeElement typeElt = elements.getTypeElement(className); |
| printClassType(typeElt, atypeFactory); |
| } |
| } |
| |
| /** Prints the types of the class and all of its enclosing fields, methods, and inner classes. */ |
| protected static void printClassType(TypeElement typeElt, AnnotatedTypeFactory atypeFactory) { |
| assert typeElt != null; |
| |
| String simpleName = typeElt.getSimpleName().toString(); |
| // Output class info |
| AnnotatedDeclaredType type = atypeFactory.fromElement(typeElt); |
| System.out.println(simpleName + "\t" + type + "\t" + type.directSupertypes()); |
| |
| // output fields and methods |
| for (Element enclosedElt : typeElt.getEnclosedElements()) { |
| if (enclosedElt instanceof TypeElement) { |
| printClassType((TypeElement) enclosedElt, atypeFactory); |
| } |
| if (!enclosedElt.getKind().isField() && !(enclosedElt instanceof ExecutableElement)) { |
| continue; |
| } |
| AnnotatedTypeMirror memberType = atypeFactory.fromElement(enclosedElt); |
| System.out.println(simpleName + "." + enclosedElt + "\t\t" + memberType); |
| } |
| } |
| |
| /** |
| * Stores any explicit annotation in AnnotatedTypeMirrors. It doesn't have a qualifier hierarchy, |
| * so it violates most of the specifications for AnnotatedTypeMirrors and AnnotatedTypeFactorys, |
| * which may cause crashes and other unexpected behaviors. |
| */ |
| public static class GeneralAnnotatedTypeFactory extends AnnotatedTypeFactory { |
| |
| public GeneralAnnotatedTypeFactory(BaseTypeChecker checker) { |
| super(checker); |
| postInit(); |
| } |
| |
| @Override |
| public void postProcessClassTree(ClassTree tree) { |
| // Do not store the qualifiers determined by this factory. This factory adds declaration |
| // annotations as type annotations, because TypeFromElement needs to read declaration |
| // annotations and this factory blindly supports all annotations. |
| // When storing those annotation to bytecode, the compiler chokes. See testcase |
| // tests/nullness/GeneralATFStore.java |
| } |
| |
| /** Return true to support any qualifier. No handling of aliases. */ |
| @Override |
| public boolean isSupportedQualifier(AnnotationMirror a) { |
| return true; |
| } |
| |
| @Override |
| protected QualifierHierarchy createQualifierHierarchy() { |
| return new GeneralQualifierHierarchy(); |
| } |
| |
| /** |
| * A very limited QualifierHierarchy that is used for access to qualifiers from different type |
| * systems. |
| */ |
| static class GeneralQualifierHierarchy implements QualifierHierarchy { |
| |
| // Always return true |
| @Override |
| public boolean isValid() { |
| return true; |
| } |
| |
| // Return the qualifier itself instead of the top. |
| @Override |
| public AnnotationMirror getTopAnnotation(AnnotationMirror start) { |
| return start; |
| } |
| |
| // Return the qualifier itself instead of the bottom. |
| @Override |
| public AnnotationMirror getBottomAnnotation(AnnotationMirror start) { |
| return start; |
| } |
| |
| // Never find a corresponding qualifier. |
| @Override |
| public AnnotationMirror findAnnotationInSameHierarchy( |
| Collection<? extends AnnotationMirror> annotations, AnnotationMirror annotationMirror) { |
| return null; |
| } |
| |
| // Not needed - raises error. |
| @Override |
| public Set<AnnotationMirror> getTopAnnotations() { |
| throw new BugInCF("GeneralQualifierHierarchy:getTopAnnotations() shouldn't be called"); |
| } |
| |
| // Not needed - should raise error. Unfortunately, in inference we ask for bottom annotations. |
| // Return a dummy value that does no harm. |
| @Override |
| public Set<AnnotationMirror> getBottomAnnotations() { |
| // throw new BugInCF("GeneralQualifierHierarchy.getBottomAnnotations() |
| // shouldn't be called"); |
| return AnnotationUtils.createAnnotationSet(); |
| } |
| |
| // Not needed - raises error. |
| @Override |
| public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) { |
| throw new BugInCF("GeneralQualifierHierarchy.isSubtype() shouldn't be called."); |
| } |
| |
| // Not needed - raises error. |
| @Override |
| public boolean isSubtype( |
| Collection<? extends AnnotationMirror> rhs, Collection<? extends AnnotationMirror> lhs) { |
| throw new BugInCF("GeneralQualifierHierarchy.isSubtype() shouldn't be called."); |
| } |
| |
| // Not needed - raises error. |
| @Override |
| public AnnotationMirror leastUpperBound(AnnotationMirror a1, AnnotationMirror a2) { |
| throw new BugInCF("GeneralQualifierHierarchy.leastUpperBound() shouldn't be called."); |
| } |
| |
| // Not needed - raises error. |
| @Override |
| public AnnotationMirror greatestLowerBound(AnnotationMirror a1, AnnotationMirror a2) { |
| throw new BugInCF("GeneralQualifierHierarchy.greatestLowerBound() shouldn't be called."); |
| } |
| |
| @Override |
| public AnnotationMirror getPolymorphicAnnotation(AnnotationMirror start) { |
| throw new BugInCF( |
| "GeneralQualifierHierarchy.getPolymorphicAnnotation() shouldn't be called."); |
| } |
| |
| @Override |
| public boolean isPolymorphicQualifier(AnnotationMirror qualifier) { |
| return false; |
| } |
| } |
| } |
| } |