checker.src.org.checkerframework.checker.formatter.FormatterAnnotatedTypeFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker Show documentation
Show all versions of checker Show documentation
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.
package org.checkerframework.checker.formatter;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.Tree;
import java.lang.annotation.Annotation;
import java.util.IllegalFormatException;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
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;
/**
* 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<
CFValue, CFStore, FormatterTransfer, FormatterAnalysis> {
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 leastUpperBound(AnnotationMirror anno1, AnnotationMirror anno2) {
if (AnnotationUtils.areSameIgnoringValues(anno1, FORMATBOTTOM)) {
return anno2;
}
if (AnnotationUtils.areSameIgnoringValues(anno2, FORMATBOTTOM)) {
return anno1;
}
if (AnnotationUtils.areSameIgnoringValues(anno1, FORMAT)
&& AnnotationUtils.areSameIgnoringValues(anno2, FORMAT)) {
ConversionCategory[] shorterArgTypesList =
treeUtil.formatAnnotationToCategories(anno1);
ConversionCategory[] longerArgTypesList =
treeUtil.formatAnnotationToCategories(anno2);
if (shorterArgTypesList.length > longerArgTypesList.length) {
ConversionCategory[] temp = longerArgTypesList;
longerArgTypesList = shorterArgTypesList;
shorterArgTypesList = temp;
}
// From the manual:
// It is legal to use a format string with fewer format specifiers
// than required, but a warning is issued.
ConversionCategory[] resultArgTypes =
new ConversionCategory[longerArgTypesList.length];
for (int i = 0; i < shorterArgTypesList.length; ++i) {
resultArgTypes[i] =
ConversionCategory.intersect(
shorterArgTypesList[i], longerArgTypesList[i]);
}
for (int i = shorterArgTypesList.length; i < longerArgTypesList.length; ++i) {
resultArgTypes[i] = longerArgTypesList[i];
}
return treeUtil.categoriesToFormatAnnotation(resultArgTypes);
}
if (AnnotationUtils.areSameIgnoringValues(anno1, INVALIDFORMAT)
&& AnnotationUtils.areSameIgnoringValues(anno2, INVALIDFORMAT)) {
assert !anno1.getElementValues().isEmpty();
assert !anno2.getElementValues().isEmpty();
if (AnnotationUtils.areSame(anno1, anno2)) {
return anno1;
}
return treeUtil.stringToInvalidFormatAnnotation(
"("
+ treeUtil.invalidFormatAnnotationToErrorMessage(anno1)
+ " or "
+ treeUtil.invalidFormatAnnotationToErrorMessage(anno2)
+ ")");
}
return UNKNOWNFORMAT;
}
@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)) {
assert !anno1.getElementValues().isEmpty();
assert !anno2.getElementValues().isEmpty();
if (AnnotationUtils.areSame(anno1, anno2)) {
return anno1;
}
return treeUtil.stringToInvalidFormatAnnotation(
"("
+ treeUtil.invalidFormatAnnotationToErrorMessage(anno1)
+ " and "
+ treeUtil.invalidFormatAnnotationToErrorMessage(anno2)
+ ")");
}
return FORMATBOTTOM;
}
}
}