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

framework.src.org.checkerframework.common.util.report.ReportVisitor 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.common.util.report;

import java.util.List;
import java.util.Map;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;

import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.basetype.BaseTypeValidator;
import org.checkerframework.common.basetype.BaseTypeVisitor;
import org.checkerframework.common.util.report.qual.ReportCall;
import org.checkerframework.common.util.report.qual.ReportCreation;
import org.checkerframework.common.util.report.qual.ReportInherit;
import org.checkerframework.common.util.report.qual.ReportOverride;
import org.checkerframework.common.util.report.qual.ReportReadWrite;
import org.checkerframework.common.util.report.qual.ReportUse;
import org.checkerframework.common.util.report.qual.ReportWrite;
import org.checkerframework.framework.source.Result;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.TreeUtils;

import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;

public class ReportVisitor extends BaseTypeVisitor {

    /**
     * The tree kinds that should be reported.
     */
    private final String[] treeKinds;

    /**
     * The modifiers that should be reported.
     */
    private final String[] modifiers;

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

        if (checker.hasOption("reportTreeKinds")) {
            String trees = checker.getOption("reportTreeKinds");
            treeKinds = trees.split(",");
        } else {
            treeKinds = null;
        }

        if (checker.hasOption("reportModifiers")) {
            String mods = checker.getOption("reportModifiers");
            modifiers = mods.split(",");
        } else {
            modifiers = null;
        }
    }

    @SuppressWarnings("CompilerMessages") // These warnings are not translated.
    @Override
    public Void scan(Tree tree, Void p) {
        if (tree != null && treeKinds != null) {
            for (String tk : treeKinds) {
                if (tree.getKind().toString().equals(tk)) {
                    checker.report(Result.failure("Tree.Kind." + tk), tree);
                }
            }
        }
        return super.scan(tree, p);
    }

    /**
     * Check for uses of the {@link ReportUse} annotation.
     * This method has to be called for every explicit or implicit use of a type,
     * most cases are simply covered by the type validator.
     *
     * @param node the tree for error reporting only
     * @param member the element from which to start looking
     */
    private void checkReportUse(Tree node, Element member) {
        Element loop = member;
        while (loop != null) {
            boolean report = this.atypeFactory.getDeclAnnotation(loop, ReportUse.class) != null;
            if (report) {
                checker.report(Result.failure("usage", node,
                        ElementUtils.getVerboseName(loop), loop.getKind(),
                        ElementUtils.getVerboseName(member), member.getKind()), node);
                break;
            } else {
                if (loop.getKind() == ElementKind.PACKAGE) {
                    loop = ElementUtils.parentPackage(elements, (PackageElement)loop);
                    continue;
                }
            }
            // Package will always be the last iteration.
            loop = loop.getEnclosingElement();
        }
    }

    /* Would we want this? Seems redundant, as all uses of the imported
     * package should already be reported.
     * Also, how do we get an element for the import?
    public Void visitImport(ImportTree node, Void p) {
        checkReportUse(node, elem);
    }
    */

    @Override
    public Void visitClass(ClassTree node, Void p) {
        TypeElement member = TreeUtils.elementFromDeclaration(node);
        boolean report = false;
        // No need to check on the declaring class itself
        // this.atypeFactory.getDeclAnnotation(member, ReportInherit.class) != null;

        // Check whether any superclass/interface had the ReportInherit annotation.
        List suptypes = ElementUtils.getSuperTypes(elements, member);
        for (TypeElement sup : suptypes) {
            report = this.atypeFactory.getDeclAnnotation(sup, ReportInherit.class) != null;
            if (report) {
                checker.report(Result.failure("inherit", node, ElementUtils.getVerboseName(sup)), node);
            }
        }
        return super.visitClass(node, p);
    }

    @Override
    public Void visitMethod(MethodTree node, Void p) {
        ExecutableElement method = TreeUtils.elementFromDeclaration(node);
        boolean report = false;

        // Check all overridden methods.
        Map overriddenMethods =
            AnnotatedTypes.overriddenMethods(elements, atypeFactory, method);
        for (Map.Entry pair: overriddenMethods.entrySet()) {
            // AnnotatedDeclaredType overriddenType = pair.getKey();
            ExecutableElement exe = pair.getValue();
            report = this.atypeFactory.getDeclAnnotation(exe, ReportOverride.class) != null;
            if (report) {
                // Set method to report the right method, if found.
                method = exe;
                break;
            }
        }

        if (report) {
            checker.report(Result.failure("override", node,
                    ElementUtils.getVerboseName(method)), node);
        }
        return super.visitMethod(node, p);
    }

    @Override
    public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
        ExecutableElement method = TreeUtils.elementFromUse(node);
        checkReportUse(node, method);
        boolean report = this.atypeFactory.getDeclAnnotation(method, ReportCall.class) != null;

        if (!report) {
            // Find all methods that are overridden by the called method
            Map overriddenMethods =
                    AnnotatedTypes.overriddenMethods(elements, atypeFactory, method);
            for (Map.Entry pair: overriddenMethods.entrySet()) {
                // AnnotatedDeclaredType overriddenType = pair.getKey();
                ExecutableElement exe = pair.getValue();
                report = this.atypeFactory.getDeclAnnotation(exe, ReportCall.class) != null;
                if (report) {
                    // Always report the element that has the annotation.
                    // Alternative would be to always report the initial element.
                    method = exe;
                    break;
                }
            }
        }

        if (report) {
            checker.report(Result.failure("methodcall", node,
                    ElementUtils.getVerboseName(method)), node);
        }
        return super.visitMethodInvocation(node, p);
    }

    @Override
    public Void visitMemberSelect(MemberSelectTree node, Void p) {
        Element member = TreeUtils.elementFromUse(node);
        checkReportUse(node, member);
        boolean report = this.atypeFactory.getDeclAnnotation(member, ReportReadWrite.class) != null;

        if (report) {
            checker.report(Result.failure("fieldreadwrite", node,
                    ElementUtils.getVerboseName(member)), node);
        }
        return super.visitMemberSelect(node, p);
    }

    @Override
    public Void visitIdentifier(IdentifierTree node, Void p) {
        Element member = TreeUtils.elementFromUse(node);
        boolean report = this.atypeFactory.getDeclAnnotation(member, ReportReadWrite.class) != null;

        if (report) {
            checker.report(Result.failure("fieldreadwrite", node,
                    ElementUtils.getVerboseName(member)), node);
        }
        return super.visitIdentifier(node, p);
    }

    @Override
    public Void visitAssignment(AssignmentTree node, Void p) {
        Element member = TreeUtils.elementFromUse(node.getVariable());
        boolean report = this.atypeFactory.getDeclAnnotation(member, ReportWrite.class) != null;

        if (report) {
            checker.report(Result.failure("fieldwrite", node,
                    ElementUtils.getVerboseName(member)), node);
        }
        return super.visitAssignment(node, p);
    }

    @Override
    public Void visitArrayAccess(ArrayAccessTree node, Void p) {
        // TODO: should we introduce an annotation for this?
        return super.visitArrayAccess(node, p);
    }

    @Override
    public Void visitNewClass(NewClassTree node, Void p) {
        Element member = TreeUtils.elementFromUse(node);
        boolean report = this.atypeFactory.getDeclAnnotation(member, ReportCreation.class) != null;
        if (!report) {
            // If the constructor is not annotated, check whether the class is.
            member = member.getEnclosingElement();
            report = this.atypeFactory.getDeclAnnotation(member, ReportCreation.class) != null;
        }
        if (!report) {
            // Check whether any superclass/interface had the ReportCreation annotation.
            List suptypes = ElementUtils.getSuperTypes(elements, (TypeElement)member);
            for (TypeElement sup : suptypes) {
                report = this.atypeFactory.getDeclAnnotation(sup, ReportCreation.class) != null;
                if (report) {
                    // Set member to report the right member if found
                    member = sup;
                    break;
                }
            }
        }

        if (report) {
            checker.report(Result.failure("creation", node, ElementUtils.getVerboseName(member)), node);
        }
        return super.visitNewClass(node, p);
    }

    @Override
    public Void visitNewArray(NewArrayTree node, Void p) {
        // TODO Should we report this if the array type is @ReportCreation?
        return super.visitNewArray(node, p);
    }

    @Override
    public Void visitTypeCast(TypeCastTree node, Void p) {
        // TODO Is it worth adding a separate annotation for this?
        return super.visitTypeCast(node, p);
    }

    @Override
    public Void visitInstanceOf(InstanceOfTree node, Void p) {
        // TODO Is it worth adding a separate annotation for this?
        return super.visitInstanceOf(node, p);
    }

    @SuppressWarnings("CompilerMessages") // These warnings are not translated.
    @Override
    public Void visitModifiers(ModifiersTree node, Void p) {
        if (node != null && modifiers != null) {
            for (Modifier hasmod : node.getFlags()) {
                for (String searchmod : modifiers) {
                    if (hasmod.toString().equals(searchmod)) {
                        checker.report(Result.failure("Modifier." + hasmod), node);
                    }
                }
            }
        }
        return super.visitModifiers(node, p);
    }

    @Override
    protected BaseTypeValidator createTypeValidator() {
        return new ReportTypeValidator(checker, this, atypeFactory);
    }

    protected class ReportTypeValidator extends BaseTypeValidator {
        public ReportTypeValidator(BaseTypeChecker checker,
                BaseTypeVisitor visitor, AnnotatedTypeFactory atypeFactory) {
            super(checker, visitor, atypeFactory);
        }

        @Override
        public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) {
            Element member = type.getUnderlyingType().asElement();
            checkReportUse(tree, member);

            return super.visitDeclared(type, tree);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy