/* * This file is part of the OpenJML project. * Author: David R. Cok * Reviewed: 2016-12-12 */ package com.sun.tools.javac.comp; import static com.sun.tools.javac.tree.JCTree.Tag.APPLY; import org.jmlspecs.annotation.NonNull; import org.jmlspecs.openjml.JmlTokenKind; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Kinds; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.TypeTag; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCTypeCast; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; /** The Check class is specialized for JML in order to avoid unchecked-cast warnings * for uses of casts in JML expressions. JML checks these logically. Also adjusts * warnings on use of old in method specifications. * <p> * [TODO: Give examples for clarity] * @author David R. Cok * */ public class JmlCheck extends Check { /** Creates a new instance - but use instance(), not this constructor, in order to * get the unique instance for the current compilation context. * @param context the compilation context this instance is for */ protected JmlCheck(@NonNull Context context) { super(context); this.context = context; } /** Registers a singleton factory for JmlCheck against the checkKey, so that there is * just one instance per context. * @param context the context in which to register the instance */ public static void preRegister(final Context context) { context.put(Check.checkKey, new Context.Factory<Check>() { public Check make(Context context) { return new JmlCheck(context); // Registers itself on construction } }); } /** Returns the instance for the given context * * @param context the context in which we are working * @return the non-null instance of JmlCheck for this context */ public static JmlCheck instance(Context context) { Check instance = context.get(checkKey); if (instance == null) instance = new JmlCheck(context); // Registers itself in the super constructor return (JmlCheck)instance; // If the registered instance is only a Check, something is catastrophically wrong } /** Set externally in order to control errors about old variables needing to be static. */ public boolean staticOldEnv = false; /** Set by setInJml in order to avoid errors about generic casts.*/ protected boolean isInJml = false; /** public method to control the isInJml flag; returns the previous value */ public boolean setInJml(Boolean inJml) { boolean b = isInJml; isInJml = inJml; return b; } // /** A warning object that issues no warnings.*/ // public static class NoWarningsAtAll extends Warner { // public void warnUnchecked() { // } // public void silentUnchecked() { // } // } // FIXME - the overriding method seems to do the same thing as the super method /** Overridden to avoid generic cast warnings in JML. */ @Override protected Type checkCastable(DiagnosticPosition pos, Type found, Type req) { if (!isInJml) return super.checkCastable(pos,found,req); if (types.isCastable(found, req, castWarner(pos, found, req))) { return req; } else { basicHandler.report(pos, diags.fragment("inconvertible.types", found, req)); return types.createErrorType(found); } } /** Overridden to avoid errors about static-ness of old variables in * method specifications and to remove static from instance declarations. */ @Override long checkFlags(DiagnosticPosition pos, long flags, Symbol sym, JCTree tree) { if (sym.kind == Kinds.ERR) return flags; if (staticOldEnv) flags &= ~Flags.STATIC; long k = super.checkFlags(pos,flags,sym,tree); if (staticOldEnv) { k |= Flags.STATIC; } if (tree instanceof JCTree.JCVariableDecl) { JCTree.JCVariableDecl d =(JCTree.JCVariableDecl) tree; boolean isInstance = JmlAttr.instance(context).findMod(d.mods,JmlTokenKind.INSTANCE) != null; if (isInstance) k &= ~Flags.STATIC; } return k; } @Override protected boolean is292targetTypeCast(JCTypeCast tree) { // OPENJML - changed from private to protected JCExpression expr = TreeInfo.skipParens(tree.expr); if (expr.hasTag(APPLY)) { JCMethodInvocation apply = (JCMethodInvocation)expr; if (apply.meth == null) return false; // OPENJML - Overridden to add this check; apply.meth is null for function-like JML backslash operators } return super.is292targetTypeCast(tree); } @Override public Type checkType(DiagnosticPosition pos, Type found, Type req) { if (found != null && found.getTag() == TypeTag.ARRAY && req.getTag() == TypeTag.ARRAY && found.toString().equals("org.jmlspecs.utils.IJMLTYPE[]") && req.toString().equals("\\TYPE[]")) { // FIXME - can we do the above without converting to String // We need this for the implementation of JML.typeargs, but // does it cause problems elsewhere? return req; } return super.checkType(pos, found, req); } boolean noDuplicateWarn = false; DiagnosticPosition duplicateErrorPosition = null; void duplicateError(DiagnosticPosition pos, Symbol sym) { if (noDuplicateWarn) return; super.duplicateError(pos, sym); } // Overridden so that we can hide warnings about not-really-duplicate declarations, when needed @Override void varargsDuplicateError(DiagnosticPosition pos, Symbol sym1, Symbol sym2) { if (!noDuplicateWarn) super.varargsDuplicateError(pos, sym1, sym2); } // Symbol findClassName(DiagnosticPosition pos, Name name, Scope s) { // for (Scope.Entry e = s.lookup(name); e.scope == s; e = e.next()) { // if (e.sym.kind == TYP && e.sym.name != names.error) { // return e.sym; // } // } // for (Symbol sym = s.owner; sym != null; sym = sym.owner) { // if (sym.kind == TYP && sym.name == name && sym.name != names.error) { // return sym; // } // } // return null; // } }