checker.src.org.checkerframework.checker.i18nformatter.I18nFormatterVisitor 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.i18nformatter;
/*>>>
import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey;
*/
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.formatter.FormatterTreeUtil.InvocationType;
import org.checkerframework.checker.formatter.FormatterTreeUtil.Result;
import org.checkerframework.checker.i18nformatter.I18nFormatterTreeUtil.FormatType;
import org.checkerframework.checker.i18nformatter.I18nFormatterTreeUtil.I18nFormatCall;
import org.checkerframework.checker.i18nformatter.qual.I18nConversionCategory;
import org.checkerframework.checker.i18nformatter.qual.I18nFormatFor;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.basetype.BaseTypeVisitor;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.javacutil.AnnotationUtils;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
/**
* Whenever a method with {@link I18nFormatFor} annotation is invoked,
* it will perform the format string verification.
*
* @checker_framework.manual #i18n-formatter-checker Internationalization
* Format String Checker
* @author Siwakorn Srisakaokul
*/
public class I18nFormatterVisitor extends BaseTypeVisitor {
public I18nFormatterVisitor(BaseTypeChecker checker) {
super(checker);
}
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
MethodInvocationNode nodeNode = (MethodInvocationNode)atypeFactory.getNodeForTree(node);
I18nFormatterTreeUtil tu = atypeFactory.treeUtil;
I18nFormatCall fc = tu.createFormatForCall(node, nodeNode, atypeFactory);
if (fc != null) {
checkInvocationFormatFor(fc);
return p;
}
return super.visitMethodInvocation(node, p);
}
private void checkInvocationFormatFor(I18nFormatCall fc) {
I18nFormatterTreeUtil tu = atypeFactory.treeUtil;
Result type = fc.getFormatType();
Result invc;
I18nConversionCategory[] formatCats;
switch (type.value()) {
case I18NINVALID:
tu.failure(type, "i18nformat.string.invalid", fc.getInvalidError());
break;
case I18NFORMATFOR:
if (!fc.isValidFormatForInvocation()) {
Result failureType = fc.getInvalidInvocationType();
tu.failure(failureType, "i18nformat.invalid.formatfor");
}
break;
case I18NFORMAT:
invc = fc.getInvocationType();
formatCats = fc.getFormatCategories();
switch (invc.value()) {
case VARARG:
Result[] paramTypes = fc.getParamTypes();
int paraml = paramTypes.length;
int formatl = formatCats.length;
// For assignments, i18nformat.missing.arguments and
// i18nformat.excess.arguments are issued
// from commonAssignmentCheck.
if (paraml < formatl) {
tu.warning(invc, "i18nformat.missing.arguments", formatl, paraml);
}
if (paraml > formatl) {
tu.warning(invc, "i18nformat.excess.arguments", formatl, paraml);
}
for (int i = 0; i < formatl && i < paraml; ++i) {
I18nConversionCategory formatCat = formatCats[i];
Result param = paramTypes[i];
TypeMirror paramType = param.value();
switch (formatCat) {
case UNUSED:
tu.warning(param, "i18nformat.argument.unused", " " + (1 + i));
break;
case GENERAL:
break;
default:
if (!fc.isValidParameter(formatCat, paramType)) {
tu.failure(param, "argument.type.incompatible", paramType, formatCat);
}
}
}
break;
case NULLARRAY:
// fall-through
case ARRAY:
for (I18nConversionCategory cat : formatCats) {
if (cat == I18nConversionCategory.UNUSED) {
tu.warning(invc, "i18nformat.argument.unused", "");
}
}
tu.warning(invc, "i18nformat.indirect.arguments");
break;
default:
break;
}
break;
default:
break;
}
}
@Override
protected void commonAssignmentCheck(AnnotatedTypeMirror varType,
AnnotatedTypeMirror valueType, Tree valueTree, /*@CompilerMessageKey*/ String errorKey) {
AnnotationMirror rhs = valueType.getAnnotationInHierarchy(atypeFactory.I18NUNKNOWNFORMAT);
AnnotationMirror lhs = varType.getAnnotationInHierarchy(atypeFactory.I18NUNKNOWNFORMAT);
// i18nformat.missing.arguments and i18nformat.excess.arguments are issued here for assignments.
// For method calls, they are issued in checkInvocationFormatFor.
if (AnnotationUtils.areSameIgnoringValues(rhs, atypeFactory.I18NFORMAT) &&
AnnotationUtils.areSameIgnoringValues(lhs, atypeFactory.I18NFORMAT)) {
I18nConversionCategory[] rhsArgTypes =
atypeFactory.treeUtil.formatAnnotationToCategories(rhs);
I18nConversionCategory[] lhsArgTypes =
atypeFactory.treeUtil.formatAnnotationToCategories(lhs);
if (rhsArgTypes.length < lhsArgTypes.length) {
// From the manual:
// It is legal to use a format string with fewer format specifiers
// than required, but a warning is issued.
checker.report(org.checkerframework.framework.source.Result.warning("i18nformat.missing.arguments",
varType.toString(), valueType.toString()), valueTree);
} else if (rhsArgTypes.length > lhsArgTypes.length) {
// Since it is known that too many conversion categories were provided,
// issue a more specific error message to that effect than assignment.type.incompatible.
checker.report(org.checkerframework.framework.source.Result.failure("i18nformat.excess.arguments",
varType.toString(), valueType.toString()), valueTree);
}
}
// By calling super.commonAssignmentCheck last, any i18nformat.excess.arguments message
// issued for a given line of code will take precedence over the assignment.type.incompatible
// issued by super.commonAssignmentCheck.
super.commonAssignmentCheck(varType, valueType, valueTree, errorKey);
}
}