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

checker.src.org.checkerframework.checker.formatter.FormatterAnnotatedTypeFactory 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.checker.formatter;

import org.checkerframework.checker.formatter.qual.ConversionCategory;
import org.checkerframework.checker.formatter.qual.Format;
import org.checkerframework.checker.formatter.qual.FormatBottom;
import org.checkerframework.checker.formatter.qual.InvalidFormat;
import org.checkerframework.checker.formatter.qual.UnknownFormat;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.framework.flow.CFStore;
import org.checkerframework.framework.flow.CFValue;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.GenericAnnotatedTypeFactory;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.TreeAnnotator;
import org.checkerframework.framework.util.GraphQualifierHierarchy;
import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory;
import org.checkerframework.javacutil.AnnotationUtils;

import java.lang.annotation.Annotation;
import java.util.IllegalFormatException;
import java.util.Set;

import javax.lang.model.element.AnnotationMirror;

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

/**
 * Adds {@link Format} to the type of tree, if it is a {@code String} or
 * {@code char} literal that represents a satisfiable format. The annotation's
 * value is set to be a list of appropriate {@link ConversionCategory} values
 * for every parameter of the format.
 *
 * @see ConversionCategory
 *
 * @author Konstantin Weitz
 */
public class FormatterAnnotatedTypeFactory extends
        GenericAnnotatedTypeFactory {

    protected final AnnotationMirror UNKNOWNFORMAT;
    protected final AnnotationMirror FORMAT;
    protected final AnnotationMirror INVALIDFORMAT;
    protected final AnnotationMirror FORMATBOTTOM;

    protected final FormatterTreeUtil treeUtil;

    public FormatterAnnotatedTypeFactory(BaseTypeChecker checker) {
        super(checker);

        UNKNOWNFORMAT = AnnotationUtils.fromClass(elements, UnknownFormat.class);
        FORMAT = AnnotationUtils.fromClass(elements, Format.class);
        INVALIDFORMAT = AnnotationUtils.fromClass(elements, InvalidFormat.class);
        FORMATBOTTOM = AnnotationUtils.fromClass(elements, FormatBottom.class);

        this.treeUtil = new FormatterTreeUtil(checker);
        this.postInit();
    }

    @Override
    protected Set> createSupportedTypeQualifiers() {
        return getBundledTypeQualifiersWithoutPolyAll(
                UnknownFormat.class, FormatBottom.class);
    }

    @Override
    public QualifierHierarchy createQualifierHierarchy(MultiGraphFactory factory) {
        return new FormatterQualifierHierarchy(factory);
    }

    @Override
    protected TreeAnnotator createTreeAnnotator() {
        return new ListTreeAnnotator(
                super.createTreeAnnotator(),
                new FormatterTreeAnnotator(this)
        );
    }

    private class FormatterTreeAnnotator extends TreeAnnotator {
        public FormatterTreeAnnotator(AnnotatedTypeFactory atypeFactory) {
            super(atypeFactory);
        }

        @Override
        public Void visitLiteral(LiteralTree tree, AnnotatedTypeMirror type) {
            if (!type.isAnnotatedInHierarchy(FORMAT)) {
                String format = null;
                if (tree.getKind() == Tree.Kind.STRING_LITERAL) {
                    format = (String) tree.getValue();
                } else if (tree.getKind() == Tree.Kind.CHAR_LITERAL) {
                    format = Character.toString((Character) tree.getValue());
                }
                if (format != null) {
                    AnnotationMirror anno;
                    try {
                        ConversionCategory[] cs = FormatUtil.formatParameterCategories(format);
                        anno = FormatterAnnotatedTypeFactory.this.treeUtil.categoriesToFormatAnnotation(cs);
                    } catch (IllegalFormatException e) {
                        anno = FormatterAnnotatedTypeFactory.this.treeUtil.exceptionToInvalidFormatAnnotation(e);
                    }
                    type.addAnnotation(anno);
                }
            }
            return super.visitLiteral(tree, type);
        }
    }

    class FormatterQualifierHierarchy extends GraphQualifierHierarchy {

        public FormatterQualifierHierarchy(MultiGraphFactory f) {
            super(f, FORMATBOTTOM);
        }

        @Override
        public boolean isSubtype(AnnotationMirror rhs, AnnotationMirror lhs) {
            if (AnnotationUtils.areSameIgnoringValues(rhs, FORMAT) &&
                AnnotationUtils.areSameIgnoringValues(lhs, FORMAT)) {
                ConversionCategory[] rhsArgTypes =
                        treeUtil.formatAnnotationToCategories(rhs);
                ConversionCategory[] lhsArgTypes =
                        treeUtil.formatAnnotationToCategories(lhs);

                if (rhsArgTypes.length > lhsArgTypes.length) {
                    return false;
                }

                for (int i = 0; i < rhsArgTypes.length; ++i) {
                    if (!ConversionCategory.isSubsetOf(lhsArgTypes[i], rhsArgTypes[i])) {
                        return false;
                    }
                }
                return true;
            }
            if (AnnotationUtils.areSameIgnoringValues(lhs, FORMAT)) {
                lhs = FORMAT;
            }
            if (AnnotationUtils.areSameIgnoringValues(rhs, FORMAT)) {
                rhs = FORMAT;
            }
            if (AnnotationUtils.areSameIgnoringValues(lhs, INVALIDFORMAT)) {
                lhs = INVALIDFORMAT;
            }
            if (AnnotationUtils.areSameIgnoringValues(rhs, INVALIDFORMAT)) {
                rhs = INVALIDFORMAT;
            }

            return super.isSubtype(rhs, lhs);
        }

        @Override
        public AnnotationMirror greatestLowerBound(AnnotationMirror anno1,
                AnnotationMirror anno2) {
            if (AnnotationUtils.areSameIgnoringValues(anno1, UNKNOWNFORMAT)) {
                return anno2;
            }
            if (AnnotationUtils.areSameIgnoringValues(anno2, UNKNOWNFORMAT)) {
                return anno1;
            }
            if (AnnotationUtils.areSameIgnoringValues(anno1, FORMAT) &&
                AnnotationUtils.areSameIgnoringValues(anno2, FORMAT)) {
                ConversionCategory[] anno1ArgTypes =
                        treeUtil.formatAnnotationToCategories(anno1);
                ConversionCategory[] anno2ArgTypes =
                        treeUtil.formatAnnotationToCategories(anno2);

                // From the manual:
                // It is legal to use a format string with fewer format specifiers
                // than required, but a warning is issued.
                int length = anno1ArgTypes.length;
                if (anno2ArgTypes.length < length) {
                    length = anno2ArgTypes.length;
                }

                ConversionCategory[] anno3ArgTypes =
                        new ConversionCategory[length];

                for (int i = 0; i < length; ++i) {
                    anno3ArgTypes[i] = ConversionCategory.union(anno1ArgTypes[i], anno2ArgTypes[i]);
                }
                return treeUtil.categoriesToFormatAnnotation(anno3ArgTypes);
            }
            if (AnnotationUtils.areSameIgnoringValues(anno1, INVALIDFORMAT) &&
                AnnotationUtils.areSameIgnoringValues(anno2, INVALIDFORMAT)) {
                if (AnnotationUtils.areSame(anno1, anno2)) {
                    return anno1;
                }
                return INVALIDFORMAT;
            }

            return FORMATBOTTOM;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy