All Downloads are FREE. Search and download functionalities are using the official Maven repository.

framework.src.org.checkerframework.framework.type.treeannotator.ImplicitsTreeAnnotator Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java’s type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.42.0
Show newest version
package org.checkerframework.framework.type.treeannotator;

import org.checkerframework.framework.qual.ImplicitFor;
import org.checkerframework.framework.qual.LiteralKind;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ErrorReporter;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.lang.model.element.AnnotationMirror;

import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;


/**
 * Adds annotations to a type based on the contents of a tree. By default, this
 * class honors the {@link org.checkerframework.framework.qual.ImplicitFor} annotation and applies implicit
 * annotations specified by {@link org.checkerframework.framework.qual.ImplicitFor} for any tree whose visitor is
 * not overridden or does not call {@code super}; it is designed to be invoked
 * from
 * {@link org.checkerframework.framework.type.AnnotatedTypeFactory#addComputedTypeAnnotations(javax.lang.model.element.Element, org.checkerframework.framework.type.AnnotatedTypeMirror)}
 * and {@link org.checkerframework.framework.type.AnnotatedTypeFactory#addComputedTypeAnnotations(com.sun.source.tree.Tree, org.checkerframework.framework.type.AnnotatedTypeMirror)}.
 *
 * 

* * {@link ImplicitsTreeAnnotator} does not traverse trees deeply by default. * * This class takes care of three of the attributes of {@link org.checkerframework.framework.qual.ImplicitFor}; * the others are handled in {@link org.checkerframework.framework.type.typeannotator.ImplicitsTypeAnnotator}. * TODO: we currently don't check that any attribute is set, that is, a qualifier * could be annotated as @ImplicitFor(), which might be misleading. * * @see org.checkerframework.framework.type.typeannotator.ImplicitsTypeAnnotator * @see TreeAnnotator */ public class ImplicitsTreeAnnotator extends TreeAnnotator { /* The following three fields are mappings from a particular AST kind, * AST Class, or String literal pattern to the set of AnnotationMirrors * that should be defaulted. * There can be at most one qualifier per qualifier hierarchy. * For type systems with single top qualifiers, the sets will always contain * at most one element. */ private final Map> treeKinds; private final Map, Set> treeClasses; private final Map> stringPatterns; protected final QualifierHierarchy qualHierarchy; /** * Map of {@link LiteralKind}s to {@link Tree.Kind}s. * This is here and not in LiteralKinds because LiteralKind is in the checker-qual.jar * which cannot depend on classes, such as Tree.Kind, that are in the tools.jar */ private static final Map literalKindToTreeKind = new EnumMap<>(LiteralKind.class); static { literalKindToTreeKind.put(LiteralKind.BOOLEAN, Kind.BOOLEAN_LITERAL); literalKindToTreeKind.put(LiteralKind.CHAR, Kind.CHAR_LITERAL); literalKindToTreeKind.put(LiteralKind.DOUBLE, Kind.DOUBLE_LITERAL); literalKindToTreeKind.put(LiteralKind.FLOAT, Kind.FLOAT_LITERAL); literalKindToTreeKind.put(LiteralKind.INT, Kind.INT_LITERAL); literalKindToTreeKind.put(LiteralKind.LONG, Kind.LONG_LITERAL); literalKindToTreeKind.put(LiteralKind.NULL, Kind.NULL_LITERAL); literalKindToTreeKind.put(LiteralKind.STRING, Kind.STRING_LITERAL); } /** * Creates a * {@link org.checkerframework.framework.type.typeannotator.ImplicitsTypeAnnotator} * from the given checker, using that checker to determine the annotations * that are in the type hierarchy. */ public ImplicitsTreeAnnotator(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); this.treeKinds = new EnumMap>(Kind.class); this.treeClasses = new HashMap, Set>(); this.stringPatterns = new IdentityHashMap>(); this.qualHierarchy = atypeFactory.getQualifierHierarchy(); // Get type qualifiers from the checker. Set> quals = atypeFactory.getSupportedTypeQualifiers(); // For each qualifier, read the @ImplicitFor annotation and put its tree // classes and kinds into maps. for (Class qual : quals) { ImplicitFor implicit = qual.getAnnotation(ImplicitFor.class); if (implicit == null) { continue; } AnnotationMirror theQual = AnnotationUtils.fromClass(atypeFactory.getElementUtils(), qual); for (LiteralKind literalKind : implicit.literals()) { addLiteralKind(literalKind, theQual); } for (String pattern : implicit.stringPatterns()) { addStringPattern(pattern, theQual); } } } /** * Added an implicit rule for a particular {@link Tree} class * @param treeClass tree class that should be implicited to {@code theQual} * @param theQual the {@code AnnotationMirror} that should be applied to the {@code treeClass} */ public void addTreeClass(Class treeClass, AnnotationMirror theQual) { boolean res = qualHierarchy.updateMappingToMutableSet(treeClasses, treeClass, theQual); if (!res) { ErrorReporter.errorAbort("PropagationTreeAnnotator: invalid update of map " + treeClasses + " at " + treeClass + " with " +theQual); } } /** * Added an implicit rule for a particular {@link LiteralKind} * @param literalKind {@code LiteralKind} that should be implicited to {@code theQual} * @param theQual the {@code AnnotationMirror} that should be applied to the {@code literalKind} */ public void addLiteralKind(LiteralKind literalKind, AnnotationMirror theQual) { if (literalKind == LiteralKind.ALL) { for (LiteralKind iterLiteralKind : LiteralKind.allLiteralKinds()) { addLiteralKind(iterLiteralKind, theQual); } } else if (literalKind == LiteralKind.PRIMITIVE) { for (LiteralKind iterLiteralKind : LiteralKind.primitiveLiteralKinds()) { addLiteralKind(iterLiteralKind, theQual); } } else { Tree.Kind treeKind = literalKindToTreeKind.get(literalKind); if (treeKind != null) { addTreeKind(treeKind, theQual); } else { ErrorReporter.errorAbort("LiteralKind " + literalKind + " is not mapped to a Tree.Kind."); } } } /** * Added an implicit rule for a particular {@link Tree.Kind} * @param treeKind {@code Tree.Kind} that should be implicited to {@code theQual} * @param theQual the {@code AnnotationMirror} that should be applied to the {@code treeKind} */ public void addTreeKind(Kind treeKind, AnnotationMirror theQual) { boolean res = qualHierarchy.updateMappingToMutableSet(treeKinds, treeKind, theQual); if (!res) { ErrorReporter.errorAbort("PropagationTreeAnnotator: invalid update of treeKinds " + treeKinds + " at " + treeKind + " with " + theQual); } } /** * Added an implicit rule for all String literals that match the given pattern * @param pattern pattern to match Strings against * @param theQual {@code AnnotationMirror} to apply to Strings that match the pattern */ public void addStringPattern(String pattern, AnnotationMirror theQual) { boolean res = qualHierarchy.updateMappingToMutableSet(stringPatterns, Pattern.compile(pattern), theQual); if (!res) { ErrorReporter.errorAbort("PropagationTreeAnnotator: invalid update of stringPatterns " + stringPatterns + " at " + pattern + " with " + theQual); } } @Override public Void defaultAction(Tree tree, AnnotatedTypeMirror type) { if (tree == null || type == null) return null; // If this tree's kind is in treeKinds, annotate the type. // If this tree's class or any of its interfaces are in treeClasses, // annotate the type, and if it was an interface add a mapping for it to // treeClasses. if (treeKinds.containsKey(tree.getKind())) { Set fnd = treeKinds.get(tree.getKind()); type.addMissingAnnotations(fnd); } else if (!treeClasses.isEmpty()) { Class t = tree.getClass(); if (treeClasses.containsKey(t)) { Set fnd = treeClasses.get(t); type.addMissingAnnotations(fnd); } for (Class c : t.getInterfaces()) { if (treeClasses.containsKey(c)) { Set fnd = treeClasses.get(c); type.addMissingAnnotations(fnd); treeClasses.put(t, treeClasses.get(c)); } } } return null; } /** * Go through the string patterns and add the greatest lower bound of all matching patterns. */ @Override public Void visitLiteral(LiteralTree tree, AnnotatedTypeMirror type) { if (!stringPatterns.isEmpty() && tree.getKind() == Kind.STRING_LITERAL) { List> matches = new ArrayList<>(); List> nonMatches = new ArrayList<>(); String string = (String) tree.getValue(); for (Pattern pattern : stringPatterns.keySet()) { Set sam = stringPatterns.get(pattern); if (pattern.matcher(string).matches()) { matches.add(sam); } else { nonMatches.add(sam); } } Set res = null; if (! matches.isEmpty()) { res = matches.get(0); for (Set sam : matches) { res = qualHierarchy.greatestLowerBounds(res, sam); } // Verify that res is not a subtype of any type in nonMatches for (Set sam: nonMatches) { if (qualHierarchy.isSubtype(res, sam)) { ErrorReporter.errorAbort( "Bug in @ImplicitFor(stringpatterns=...) in type hierarchy definition: inferred type for \"" + string + "\" is " + res + " which is a subtype of " + sam + " but its pattern does not match the string. matches = " + matches + "; nonMatches = " + nonMatches); } } type.addAnnotations(res); } } return super.visitLiteral(tree, type); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy