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

checker.src.org.checkerframework.checker.i18nformatter.I18nFormatterVisitor 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.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);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy