/* * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package org.visage.tools.comp; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.Collection; import com.sun.tools.mjavac.code.*; import com.sun.tools.mjavac.code.Lint.LintCategory; import com.sun.tools.mjavac.code.Symbol.*; import com.sun.tools.mjavac.code.Type.ClassType; import com.sun.tools.mjavac.code.Type.ErrorType; import com.sun.tools.mjavac.code.Type.ForAll; import com.sun.tools.mjavac.code.Type.TypeVar; import com.sun.tools.mjavac.code.Type.MethodType; import static com.sun.tools.mjavac.code.Flags.*; import static com.sun.tools.mjavac.code.Kinds.*; import static com.sun.tools.mjavac.code.TypeTags.*; import static com.sun.tools.mjavac.code.TypeTags.WILDCARD; import com.sun.tools.mjavac.comp.Infer; import com.sun.tools.mjavac.jvm.ByteCodes; import com.sun.tools.mjavac.jvm.ClassReader; import com.sun.tools.mjavac.jvm.Target; import com.sun.tools.mjavac.util.*; import com.sun.tools.mjavac.util.JCDiagnostic.DiagnosticPosition; import org.visage.api.VisageBindStatus; import org.visage.tools.code.VisageClassSymbol; import org.visage.tools.code.VisageVarSymbol; import org.visage.tools.code.VisageFlags; import org.visage.tools.code.VisageSymtab; import org.visage.tools.code.VisageTypes; import org.visage.tools.comp.VisageAttr.Sequenceness; import org.visage.tools.tree.*; import org.visage.tools.tree.VisageTreeScanner; import org.visage.tools.util.MsgSym; import static org.visage.tools.code.VisageFlags.*; /** Type checking helper class for the attribution phase. * * <p><b>This is NOT part of any API supported by Sun Microsystems. If * you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice.</b> */ public class VisageCheck { protected static final Context.Key<VisageCheck> visageCheckKey = new Context.Key<VisageCheck>(); private final VisageDefs defs; private final Name.Table names; private final Log log; private final JCDiagnostic.Factory diags; private final Messages messages; private final Options options; private final VisageSymtab syms; private final Infer infer; private final Target target; private final Source source; private final VisageTypes types; private final VisageAttr attr; private final VisageTreeInfo treeinfo; private final VisageResolve rs; // The set of lint options currently in effect. It is initialized // from the context, and then is set/reset as needed by Attr as it // visits all the various parts of the trees during attribution. private Lint lint; enum WriteKind { ASSIGN, INIT_NON_BIND, INIT_BIND, VAR_QUERY } public static VisageCheck instance(Context context) { VisageCheck instance = context.get(visageCheckKey); if (instance == null) instance = new VisageCheck(context); return instance; } public static void preRegister(final Context context) { context.put(visageCheckKey, new Context.Factory<VisageCheck>() { public VisageCheck make() { return new VisageCheck(context); } }); } protected VisageCheck(Context context) { context.put(visageCheckKey, this); defs = VisageDefs.instance(context); names = Name.Table.instance(context); log = Log.instance(context); diags = JCDiagnostic.Factory.instance(context); messages = Messages.instance(context); syms = (VisageSymtab) Symtab.instance(context); infer = Infer.instance(context); types = VisageTypes.instance(context); attr = VisageAttr.instance(context); options = Options.instance(context); target = Target.instance(context); source = Source.instance(context); lint = Lint.instance(context); treeinfo = (VisageTreeInfo)VisageTreeInfo.instance(context); allowGenerics = source.allowGenerics(); //allowAnnotations = source.allowAnnotations(); complexInference = options.get("-complexinference") != null; boolean verboseDeprecated = lint.isEnabled(LintCategory.DEPRECATION); boolean verboseUnchecked = lint.isEnabled(LintCategory.UNCHECKED); deprecationHandler = new MandatoryWarningHandler(log,verboseDeprecated, MsgSym.MESSAGEPREFIX_DEPRECATED); warnOnUsePackageHandler = new MandatoryWarningHandler(log, true, MsgSym.MESSAGEPREFIX_WARNONUSE); uncheckedHandler = new MandatoryWarningHandler(log, verboseUnchecked, MsgSym.MESSAGEPREFIX_UNCHECKED); rs = VisageResolve.instance(context); } /** Switch: generics enabled? */ boolean allowGenerics; /** Switch: annotations enabled? */ //boolean allowAnnotations; /** Switch: -complexinference option set? */ boolean complexInference; /** A table mapping flat names of all compiled classes in this run to their * symbols; maintained from outside. */ public Map<Name,ClassSymbol> compiled = new HashMap<Name, ClassSymbol>(); /** A handler for messages about deprecated usage. */ private MandatoryWarningHandler deprecationHandler; /** A handler for messages about -XDwarnOnUse package usage. */ private MandatoryWarningHandler warnOnUsePackageHandler; /** A handler for messages about unchecked or unsafe usage. */ private MandatoryWarningHandler uncheckedHandler; /* ************************************************************************* * Errors and Warnings **************************************************************************/ Lint setLint(Lint newLint) { Lint prev = lint; lint = newLint; return prev; } /** Warn about deprecated symbol. * @param pos Position to be used for error reporting. * @param sym The deprecated symbol. */ void warnDeprecated(DiagnosticPosition pos, Symbol sym) { if (!lint.isSuppressed(LintCategory.DEPRECATION)) deprecationHandler.report(pos, MsgSym.MESSAGE_HAS_BEEN_DEPRECATED, sym, sym.location()); } /** Warn about a -XDwarnOnUse package symbol. * @param pos Position to be used for error reporting. * @param sym The deprecated symbol. */ void warnWarnOnUsePackage(DiagnosticPosition pos, Symbol sym) { warnOnUsePackageHandler.report(pos, MsgSym.MESSAGE_VISAGE_WARN_ON_USE_PACKAGE, sym); } /** Warn about unchecked operation. * @param pos Position to be used for error reporting. * @param msg A string describing the problem. */ public void warnUnchecked(DiagnosticPosition pos, String msg, Object... args) { if (!lint.isSuppressed(LintCategory.UNCHECKED)) uncheckedHandler.report(pos, msg, args); } /** * Report any deferred diagnostics. */ public void reportDeferredDiagnostics() { deprecationHandler.reportDeferredDiagnostic(); warnOnUsePackageHandler.reportDeferredDiagnostic(); uncheckedHandler.reportDeferredDiagnostic(); } /** Report a failure to complete a class. * @param pos Position to be used for error reporting. * @param ex The failure to report. */ public Type completionError(DiagnosticPosition pos, CompletionFailure ex) { log.error(pos, MsgSym.MESSAGE_CANNOT_ACCESS, ex.sym, ex.errmsg); if (ex instanceof ClassReader.BadClassFile) throw new Abort(); else return syms.errType; } /** Report a type error. * @param pos Position to be used for error reporting. * @param problem A string describing the error. * @param found The type that was found. * @param req The type that was required. */ Type typeError(DiagnosticPosition pos, Object problem, Type found, Type req) { String foundAsVisageType = types.toVisageString(found); String requiredAsVisageType = types.toVisageString(req); log.error(pos, MsgSym.MESSAGE_PROB_FOUND_REQ, problem, foundAsVisageType, requiredAsVisageType); return syms.errType; } Type typeError(DiagnosticPosition pos, Object problem, Object found, Object req) { Object requiredAsVisageType = req; if (req instanceof Type) { requiredAsVisageType = types.toVisageString((Type) requiredAsVisageType); } Object foundAsVisageType = found; if (foundAsVisageType instanceof Type) { foundAsVisageType = types.toVisageString((Type) foundAsVisageType); } log.error(pos, MsgSym.MESSAGE_PROB_FOUND_REQ, problem, foundAsVisageType, requiredAsVisageType); return syms.errType; } Type typeError(DiagnosticPosition pos, String problem, Type found, Type req, Object explanation) { String foundAsVisageType = types.toVisageString(found); String requiredAsVisageType = types.toVisageString(req); log.error(pos, MsgSym.MESSAGE_PROB_FOUND_REQ_1, problem, foundAsVisageType, requiredAsVisageType, explanation); return syms.errType; } /** Report an error that wrong type tag was found. * @param pos Position to be used for error reporting. * @param required An internationalized string describing the type tag * required. * @param found The type that was found. */ Type typeTagError(DiagnosticPosition pos, Object required, Object found) { Object requiredAsVisageType = required; if (required instanceof Type) { requiredAsVisageType = types.toVisageString((Type) requiredAsVisageType); } Object foundAsVisageType = found; if (foundAsVisageType instanceof Type) { foundAsVisageType = types.toVisageString((Type) foundAsVisageType); } log.error(pos, MsgSym.MESSAGE_TYPE_FOUND_REQ, foundAsVisageType, requiredAsVisageType); return syms.errType; } /** Report an error that symbol cannot be referenced before super * has been called. * @param pos Position to be used for error reporting. * @param sym The referenced symbol. */ void earlyRefError(DiagnosticPosition pos, Symbol sym) { log.error(pos, MsgSym.MESSAGE_CANNOT_REF_BEFORE_CTOR_CALLED, sym); } /** Report duplicate declaration error. */ void duplicateError(DiagnosticPosition pos, Symbol sym) { if (sym.type == null || !sym.type.isErroneous()) { log.error(pos, MsgSym.MESSAGE_ALREADY_DEFINED, sym, types.location(sym)); } } /** Report array/varargs duplicate declaration */ void varargsDuplicateError(DiagnosticPosition pos, Symbol sym1, Symbol sym2) { if (!sym1.type.isErroneous() && !sym2.type.isErroneous()) { log.error(pos, MsgSym.MESSAGE_ARRAY_AND_VARARGS, sym1, sym2, sym2.location()); } } /* ************************************************************************ * duplicate declaration checking *************************************************************************/ /** Check that variable does not hide variable with same name in * immediately enclosing local scope. * @param pos Position for error reporting. * @param v The symbol. * @param s The scope. */ void checkTransparentVar(DiagnosticPosition pos, VisageVarSymbol v, Scope s) { if (s.next != null) { for (Scope.Entry e = s.next.lookup(v.name); e.scope != null && e.sym.owner == v.owner; e = e.next()) { if (e.sym.kind == VAR && (e.sym.owner.kind & (VAR | MTH)) != 0 && v.name != names.error) { duplicateError(pos, e.sym); return; } } } } /** Check that a class or interface does not hide a class or * interface with same name in immediately enclosing local scope. * @param pos Position for error reporting. * @param c The symbol. * @param s The scope. */ void checkTransparentClass(DiagnosticPosition pos, ClassSymbol c, Scope s) { if (s.next != null) { for (Scope.Entry e = s.next.lookup(c.name); e.scope != null && e.sym.owner == c.owner; e = e.next()) { if (e.sym.kind == TYP && (e.sym.owner.kind & (VAR | MTH)) != 0 && c.name != names.error) { duplicateError(pos, e.sym); return; } } } } /** Check that class does not have the same name as one of * its enclosing classes, or as a class defined in its enclosing scope. * return true if class is unique in its enclosing scope. * @param pos Position for error reporting. * @param name The class name. * @param s The enclosing scope. */ boolean checkUniqueClassName(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) { duplicateError(pos, e.sym); return false; } } for (Symbol sym = s.owner; sym != null; sym = sym.owner) { if (sym.kind == TYP && sym.name == name && sym.name != names.error) { duplicateError(pos, sym); return true; } } return true; } /* ************************************************************************* * Class name generation **************************************************************************/ /** Return name of local class. * This is of the form <enclClass> $ n <classname> * where * enclClass is the flat name of the enclosing class, * classname is the simple name of the local class */ Name localClassName(ClassSymbol c) { for (int i=1; ; i++) { Name flatname = names. fromString("" + c.owner.enclClass().flatname + target.syntheticNameChar() + i + c.name); if (compiled.get(flatname) == null) return flatname; } } /* ************************************************************************* * Type Checking **************************************************************************/ /** Check that a given type is assignable to a given proto-type. * If it is, return the type, otherwise return errType. * @param pos Position to be used for error reporting. * @param found The type that was found. * @param req The type that was required. */ Type checkType(DiagnosticPosition pos, Type foundRaw, Type reqRaw, Sequenceness pSequenceness) { return checkType(pos, foundRaw, reqRaw, pSequenceness, true); } Type checkType(DiagnosticPosition pos, Type found, Type req, Sequenceness pSequenceness, boolean giveWarnings) { Type realFound = found; if (req.tag == ERROR) return req; if (found == syms.unreachableType) return found; if (found.tag == FORALL) { if (req == syms.visage_UnspecifiedType) // Is this the right thing to do? FIXME return types.erasure(found); else return instantiatePoly(pos, (ForAll)found, req, convertWarner(pos, found, req)); } if (req.tag == NONE || req == syms.visage_UnspecifiedType) return found; if (types.isSequence(req)) { pSequenceness = Sequenceness.REQUIRED; } if (types.isSequence(found)) { if (pSequenceness == Sequenceness.DISALLOWED && ! types.isSameType(req, syms.objectType)) { log.error(pos, MsgSym.MESSAGE_VISAGE_BAD_SEQUENCE, types.toVisageString(req)); return syms.errType; } } Type reqUnboxed, foundUnboxed; if (req.tag == CLASS) { reqUnboxed = types.unboxedType(req); if (reqUnboxed.tag == NONE) reqUnboxed = req; } else reqUnboxed = req; if (found.tag == CLASS) { foundUnboxed = types.unboxedType(found); if (foundUnboxed.tag == NONE) foundUnboxed = found; } else foundUnboxed = found; if (types.isAssignable(foundUnboxed, reqUnboxed, convertWarner(pos, found, req))) { Type foundElem = types.elementTypeOrType(found); Type reqElem = types.elementTypeOrType(req); if (foundElem.tag == VOID && reqElem.tag != VOID) { return typeError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_INCOMPATIBLE_TYPES), found, req); } if (reqElem.tag <= LONG && foundElem.tag >= FLOAT && foundElem.tag <= DOUBLE && giveWarnings) { // FUTURE/FIXME: return typeError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_INCOMPATIBLE_TYPES), found, req); String foundAsVisageType = types.toVisageString(foundUnboxed); String requiredAsVisageType = types.toVisageString(reqUnboxed); log.warning(pos, MsgSym.MESSAGE_PROB_FOUND_REQ, JCDiagnostic.fragment(MsgSym.MESSAGE_POSSIBLE_LOSS_OF_PRECISION), foundAsVisageType, requiredAsVisageType); } return realFound; } // use the VisageClassSymbol's supertypes to see if req is in the supertypes of found. for (Type baseType : types.supertypesClosure(found, true)) { if (types.isAssignable(baseType, req, convertWarner(pos, found, req))) return realFound; } Type foundElem = types.elementTypeOrType(found); Type reqElem = types.elementTypeOrType(req); if (foundElem.tag <= DOUBLE && reqElem.tag <= DOUBLE) { if (foundElem.tag == VOID && reqElem.tag != VOID) { return typeError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_INCOMPATIBLE_TYPES), found, req); } if (giveWarnings) { String foundAsVisageType = types.toVisageString(found); String requiredAsVisageType = types.toVisageString(req); log.warning(pos.getStartPosition(), MsgSym.MESSAGE_PROB_FOUND_REQ, JCDiagnostic.fragment(MsgSym.MESSAGE_POSSIBLE_LOSS_OF_PRECISION), foundAsVisageType, requiredAsVisageType); } return realFound; } if (found.isSuperBound()) { log.error(pos, MsgSym.MESSAGE_ASSIGNMENT_FROM_SUPER_BOUND, found); return syms.errType; } if (req.isExtendsBound()) { log.error(pos, MsgSym.MESSAGE_ASSIGNMENT_TO_EXTENDS_BOUND, req); return syms.errType; } return typeError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_INCOMPATIBLE_TYPES), found, req); } /** Instantiate polymorphic type to some prototype, unless * prototype is `anyPoly' in which case polymorphic type * is returned unchanged. */ Type instantiatePoly(DiagnosticPosition pos, ForAll t, Type pt, Warner warn) { if (pt == Infer.anyPoly && complexInference) { return t; } else if (pt == Infer.anyPoly || pt.tag == NONE) { Type newpt = t.qtype.tag <= VOID ? t.qtype : syms.objectType; return instantiatePoly(pos, t, newpt, warn); } else if (pt.tag == ERROR) { return pt; } else { try { return infer.instantiateExpr(t, pt, warn); } catch (Infer.NoInstanceException ex) { if (ex.isAmbiguous) { JCDiagnostic d = ex.getDiagnostic(); log.error(pos, d!=null ? MsgSym.MESSAGE_UNDETERMINDED_TYPE_1 : MsgSym.MESSAGE_UNDETERMINDED_TYPE, t, d); return syms.errType; } else { JCDiagnostic d = ex.getDiagnostic(); return typeError(pos, JCDiagnostic.fragment(d!=null ? MsgSym.MESSAGE_INCOMPATIBLE_TYPES_1 : MsgSym.MESSAGE_INCOMPATIBLE_TYPES, d), t, pt); } } } } void checkInstanceOf(DiagnosticPosition pos, Type s, Type t) { if (!types.isCastableNoConversion(s, t, Warner.noWarnings)) { typeError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_INCONVERTIBLE_TYPES), s, t); } } /** Check that a given type can be cast to a given target type. * Return the result of the cast. * @param pos Position to be used for error reporting. * @param found The type that is being cast. * @param req The target type of the cast. */ Type checkCastable(DiagnosticPosition pos, Type found, Type req) { if (found.tag == FORALL && found instanceof ForAll) { instantiatePoly(pos, (ForAll) found, req, castWarner(pos, found, req)); return req; } else if (types.isCastable(found, req, castWarner(pos, found, req))) { return req; } else { return typeError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_INCONVERTIBLE_TYPES), found, req); } } //where /** Is type a type variable, or a (possibly multi-dimensional) array of * type variables? */ boolean isTypeVar(Type t) { return t.tag == TYPEVAR || t.tag == ARRAY && isTypeVar(types.elemtype(t)); } /** Check that a type is within some bounds. * * Used in TypeApply to verify that, e.g., X in V<X> is a valid * type argument. * @param pos Position to be used for error reporting. * @param a The type that should be bounded by bs. * @param bs The bound. */ private void checkExtends(DiagnosticPosition pos, Type a, TypeVar bs) { if (a.isUnbound()) { return; } else if (a.tag != WILDCARD) { a = types.upperBound(a); for (List<Type> l = types.getBounds(bs); l.nonEmpty(); l = l.tail) { if (!types.isSubtype(a, l.head)) { log.error(pos, MsgSym.MESSAGE_NOT_WITHIN_BOUNDS, a); return; } } } else if (a.isExtendsBound()) { if (!types.isCastable(bs.getUpperBound(), types.upperBound(a), Warner.noWarnings)) log.error(pos, MsgSym.MESSAGE_NOT_WITHIN_BOUNDS, a); } else if (a.isSuperBound()) { if (types.notSoftSubtype(types.lowerBound(a), bs.getUpperBound())) log.error(pos, MsgSym.MESSAGE_NOT_WITHIN_BOUNDS, a); } } /** Check that type is different from 'void'. * @param pos Position to be used for error reporting. * @param t The type to be checked. */ Type checkNonVoid(DiagnosticPosition pos, Type t) { if (t.tag == VOID) { log.error(pos, MsgSym.MESSAGE_VOID_NOT_ALLOWED_HERE); return syms.errType; } else { return t; } } /** Check that type is a class or interface type. * @param pos Position to be used for error reporting. * @param t The type to be checked. */ Type checkClassType(DiagnosticPosition pos, Type t) { if (t.tag != CLASS && t.tag != ERROR) return typeTagError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_TYPE_REQ_CLASS), (t.tag == TYPEVAR) ? JCDiagnostic.fragment(MsgSym.MESSAGE_TYPE_PARAMETER, t) : t); else return t; } /** Is given blank final variable assignable, i.e. in a scope where it * may be assigned to even though it is final? * @param v The blank final variable. * @param env The current environment. */ boolean isAssignableAsBlankFinal(VisageVarSymbol v, VisageEnv<VisageAttrContext> env) { Symbol owner = env.info.scope.owner; // owner refers to the innermost variable, method or // initializer block declaration at this point. return v.owner == owner || ((owner.name == names.init || // i.e. we are in a constructor owner.kind == VAR || // i.e. we are in a variable initializer (owner.flags() & BLOCK) != 0) // i.e. we are in an initializer block && v.owner == owner.owner && ((v.flags() & STATIC) != 0) == VisageResolve.isStatic(env)); } /** Check that variable can be assigned to. * @param pos The current source code position. * @param v The assigned varaible * @param base If the variable is referred to in a Select, the part * to the left of the `.', null otherwise. * @param env The current environment. */ void checkAssignable(DiagnosticPosition pos, VisageVarSymbol v, VisageTree base, Type site, VisageEnv<VisageAttrContext> env, WriteKind writeKind) { //TODO: for attributes they are always final -- this should really be checked in VisageClassReader //TODO: rebutal, actual we should just use a different final if ((v.flags() & FINAL) != 0 && !types.isVisageClass(v.owner) && ((v.flags() & HASINIT) != 0 || !((base == null || (base.getVisageTag() == VisageTag.IDENT && VisageTreeInfo.name(base) == names._this)) && isAssignableAsBlankFinal(v, env)))) { log.error(pos, MsgSym.MESSAGE_CANNOT_ASSIGN_VAL_TO_FINAL_VAR, v); } else if ((v.flags() & VisageFlags.IS_DEF) != 0L) { log.error(pos, MsgSym.MESSAGE_VISAGE_CANNOT_ASSIGN_TO_DEF, v); } else if ((v.flags() & Flags.PARAMETER) != 0L) { log.error(pos, MsgSym.MESSAGE_VISAGE_CANNOT_ASSIGN_TO_PARAMETER, v); } else { // now check access permissions for write/init switch (writeKind) { case INIT_NON_BIND: if ((v.flags() & VisageFlags.PUBLIC_INIT) != 0L) { // it is an initialization, and init is explicitly allowed return; } break; } if (!rs.isAccessibleForWrite(env, site, v)) { String msg; switch (writeKind) { case INIT_BIND: msg = MsgSym.MESSAGE_VISAGE_REPORT_BIND_ACCESS; break; case INIT_NON_BIND: msg = MsgSym.MESSAGE_VISAGE_REPORT_INIT_ACCESS; break; case VAR_QUERY: msg = MsgSym.MESSAGE_VISAGE_REPORT_VAR_QUERY_ACCESS; break; case ASSIGN: default: msg = MsgSym.MESSAGE_VISAGE_REPORT_WRITE_ACCESS; break; } log.error(pos, msg, v, VisageCheck.protectionString(v.flags()), v.location()); } } } /** True if we should warn in 'a.b.' that 'a' is non-constant. * We don't re-evaluate the select target * in bidirectional binds. So, we may issue warning. */ boolean checkBidiSelect(VisageSelect select, VisageEnv<VisageAttrContext> env, Type pt) { VisageTree base = select.getExpression(); // We don't re-evaluate the select target // in bidirectional binds. So, we may issue warning. // Do not warn for this.foo and super.foo Name baseName = VisageTreeInfo.name(base); if (baseName == names._this || baseName == names._super) { return false; } // Do not warn for static variable select, // because the target is a class and so that // can not change. Also, ClassName.foo is used // to access super class variable - we do not // warn that case either. Symbol sym = VisageTreeInfo.symbolFor(base); if (sym instanceof VisageClassSymbol) { return false; } // If the target of member select is a parameter, // or is a "def" variable that is not initialized // with bind, then we know the target can not change. if (base instanceof VisageIdent) { long flags = sym.flags(); boolean isParam = (flags & Flags.PARAMETER) != 0L; boolean isDef = (flags & VisageFlags.IS_DEF) != 0L; boolean isBindInit = (flags & VisageFlags.VARUSE_BOUND_INIT) != 0L; boolean targetFinal = isParam || (isDef && !isBindInit); return !targetFinal; } return true; } void checkBoundArrayVar(VisageAbstractVar tree) { if (tree.getInitializer() == null || tree.getInitializer().type == null) { return; } else if (types.isArray(tree.getInitializer().type)) { if (tree.isBound()) { log.warning(tree.pos(), MsgSym.MESSAGE_VISAGE_UNSUPPORTED_TYPE_IN_BIND); } if (tree.getOnInvalidate() != null || tree.getOnReplace() != null) { DiagnosticPosition pos = tree.getOnReplace() != null ? tree.getOnReplace().pos() : tree.getOnInvalidate().pos(); log.warning(pos, MsgSym.MESSAGE_VISAGE_UNSUPPORTED_TYPE_IN_TRIGGER); } } } void checkBidiBind(VisageExpression init, VisageBindStatus bindStatus, VisageEnv<VisageAttrContext> env, Type pt) { if (bindStatus.isBidiBind()) { Symbol initSym = null; VisageTree base = null; Type site = null; switch (init.getVisageTag()) { case IDENT: { initSym = ((VisageIdent) init).sym; base = null; site = env.enclClass.sym.type; break; } case SELECT: { VisageSelect select = (VisageSelect) init; initSym = select.sym; base = select.getExpression(); if (checkBidiSelect(select, env, pt)) log.warning(select.getExpression().pos(), MsgSym.MESSAGE_SELECT_TARGET_NOT_REEVALUATED_FOR_BIDI_BIND, select.getExpression(), select.name); site = select.type; break; } } if (initSym instanceof VarSymbol) { if (pt != null && bindStatus.isBidiBind() && !types.isSameType(pt, initSym.type)) { log.error(init.pos(), MsgSym.MESSAGE_VISAGE_WRONG_TYPE_FOR_BIDI_BIND, types.toVisageString(initSym.type), types.toVisageString(pt)); } checkAssignable(init.pos(), (VisageVarSymbol) initSym, base, site, env, WriteKind.INIT_BIND); } else { log.error(init.pos(), MsgSym.MESSAGE_VISAGE_EXPR_UNSUPPORTED_FOR_BIDI_BIND); } } } /** * Return element type for a sequence type, and report error otherwise. */ public Type checkSequenceElementType (DiagnosticPosition pos, Type t) { if (types.isSequence(t)) return types.elementType(t); if (t.tag != ERROR) { return typeTagError(pos, types.sequenceType(syms.unknownType), t); } return syms.errType; } public Type checkSequenceOrArrayType (DiagnosticPosition pos, Type t) { if (!types.isSequence(t) && t.tag != ARRAY && !t.isErroneous()) return typeTagError(pos, messages.getLocalizedString(MsgSym.MESSAGEPREFIX_COMPILER_MISC + MsgSym.MESSAGE_VISAGE_SEQ_OR_ARRAY), t); else return t; } /** * Check that a method call of the kind t.memberName() is legal. * t must be a direct supertype of the enclosing class type csym. * * @param pos the position in which the error should be reported * @param csym the enclosing class * @param t the qualifier type */ public void checkSuper(DiagnosticPosition pos, VisageClassSymbol csym, Type t) { if (types.isSameType(csym.type, t)) return; boolean isOk = false; List<Type> supertypes = types.supertypes(csym.type); if (supertypes.isEmpty()) { isOk = types.isSameType(syms.objectType, t); } else { while(supertypes.nonEmpty() && !isOk) { if (types.isSameType(t, supertypes.head)) isOk = true; supertypes = supertypes.tail; } } if (!isOk) { log.error(pos, MsgSym.MESSAGE_VISAGE_INVALID_SELECT_FOR_SUPER, types.toVisageString(t), types.toVisageString(csym.type)); } } /** Check that type is a class or interface type. * @param pos Position to be used for error reporting. * @param t The type to be checked. * @param noBounds True if type bounds are illegal here. */ Type checkClassType(DiagnosticPosition pos, Type t, boolean noBounds) { t = checkClassType(pos, t); if (noBounds && t.isParameterized()) { List<Type> args = t.getTypeArguments(); while (args.nonEmpty()) { if (args.head.tag == WILDCARD) return typeTagError(pos, Log.getLocalizedString(MsgSym.MESSAGE_TYPE_REQ_EXACT), args.head); args = args.tail; } } return t; } /** Check that type is a reifiable class, interface or array type. * @param pos Position to be used for error reporting. * @param t The type to be checked. */ Type checkReifiableReferenceType(DiagnosticPosition pos, Type t) { if (t.tag != CLASS && t.tag != ARRAY && t.tag != ERROR) { return typeTagError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_TYPE_REQ_CLASS_ARRAY), t); } else if (!types.isReifiable(t)) { log.error(pos, MsgSym.MESSAGE_ILLEGAL_GENERIC_TYPE_FOR_INSTOF); return syms.errType; } else { return t; } } /** Check that type is a reference type, i.e. a class, interface or array type * or a type variable. * @param pos Position to be used for error reporting. * @param t The type to be checked. */ Type checkRefType(DiagnosticPosition pos, Type t) { switch (t.tag) { case CLASS: case ARRAY: case TYPEVAR: case WILDCARD: case ERROR: return t; default: return typeTagError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_TYPE_REQ_REF), t); } } /** Check that type is a null or reference type. * @param pos Position to be used for error reporting. * @param t The type to be checked. */ Type checkNullOrRefType(DiagnosticPosition pos, Type t) { switch (t.tag) { case CLASS: case ARRAY: case TYPEVAR: case WILDCARD: case BOT: case ERROR: return t; default: return typeTagError(pos, JCDiagnostic.fragment(MsgSym.MESSAGE_TYPE_REQ_REF), t); } } /** Check that flag set does not contain elements of two conflicting sets. * Log error if it does. * Return true if it doesn't. * @param pos Position to be used for error reporting. * @param flags The set of flags to be checked. * @param set1 Conflicting flags set #1. * @param set2 Conflicting flags set #2. */ boolean checkDisjoint(DiagnosticPosition pos, long flags, long set1, long set2) { if ((flags & set1) != 0 && (flags & set2) != 0) { log.error(pos, MsgSym.MESSAGE_ILLEGAL_COMBINATION_OF_MODIFIERS, VisageTreeInfo.flagNames(VisageTreeInfo.firstFlag(flags & set1)), VisageTreeInfo.flagNames(VisageTreeInfo.firstFlag(flags & set2))); return false; } else return true; } /** Check that flag set does not contain elements of two conflicting sets. * Log warning if it does. * Return true if it doesn't. * @param pos Position to be used for error reporting. * @param flags The set of flags to be checked. * @param set1 Conflicting flags set #1. * @param set2 Conflicting flags set #2. */ boolean checkDisjointWarn(DiagnosticPosition pos, long flags, long set1, long set2) { if ((flags & set1) != 0 && (flags & set2) != 0) { log.warning(pos, MsgSym.MESSAGE_VISAGE_REDUNDANT_ACCESS_MODIFIERS, VisageTreeInfo.flagNames(VisageTreeInfo.firstFlag(flags & set1)), VisageTreeInfo.flagNames(VisageTreeInfo.firstFlag(flags & set2))); return false; } else return true; } /** Check that given modifiers are legal for given symbol and * return modifiers together with any implicit modifiers for that symbol. * Warning: we can't use flags() here since this method * is called during class enter, when flags() would cause a premature * completion. * @param pos Position to be used for error reporting. * @param flags The set of modifiers given in a definition. * @param sym The defined symbol. */ long checkFlags(DiagnosticPosition pos, long flags, Symbol sym, VisageTree tree) { long mask; String msg = MsgSym.MESSAGE_VISAGE_MOD_NOT_ALLOWED_ON; String thing; boolean isScriptLevel = (flags & STATIC) != 0; switch (sym.kind) { case VAR: VisageVarSymbol vsym = (VisageVarSymbol)sym; boolean isDef = ((flags & IS_DEF) != 0); thing = isDef? "def" : "var"; if (!vsym.isMember()) { mask = VisageLocalVarFlags; msg = MsgSym.MESSAGE_VISAGE_MOD_NOT_ALLOWED_ON_LOCAL; } else if (isDef) { mask = VisageMemberDefFlags; msg = MsgSym.MESSAGE_VISAGE_MOD_NOT_ALLOWED_ON; } else if (isScriptLevel) { mask = VisageScriptVarFlags; msg = MsgSym.MESSAGE_VISAGE_MOD_NOT_ALLOWED_ON_SCRIPT; } else { mask = VisageInstanceVarFlags; msg = MsgSym.MESSAGE_VISAGE_MOD_NOT_ALLOWED_ON_INSTANCE; } break; case MTH: if (isScriptLevel) { mask = VisageScriptFunctionFlags; msg = MsgSym.MESSAGE_VISAGE_MOD_NOT_ALLOWED_ON_SCRIPT; } else { mask = VisageFunctionFlags; } thing = "function"; break; case TYP: // flags aren't currently different: if (sym.isLocal()) ... mask = VisageClassFlags; thing = "class"; break; default: throw new AssertionError(); } long illegal = flags & VisageUserFlags & ~mask; /*** System.err.println(sym); System.err.printf("%022o mask -- %s\n", mask, VisageTreeInfo.flagNames(mask)); System.err.printf("%022o ~mask -- %s\n", ~mask, VisageTreeInfo.flagNames(~mask)); System.err.printf("%022o VisageUserFlags -- %s\n", VisageUserFlags, VisageTreeInfo.flagNames(VisageUserFlags)); System.err.printf("%022o flags -- %s\n", flags, VisageTreeInfo.flagNames(flags)); System.err.printf("%022o illegal -- %s\n", illegal, VisageTreeInfo.flagNames(illegal)); ***/ if (illegal != 0) { log.error(pos, msg, VisageTreeInfo.flagNames(illegal), thing); } else if ((sym.kind == TYP || checkDisjoint(pos, flags, ABSTRACT | MIXIN, PRIVATE | STATIC)) && checkDisjoint(pos, flags, ABSTRACT | INTERFACE, FINAL | NATIVE | SYNCHRONIZED) && checkDisjoint(pos, flags, PUBLIC, PRIVATE | PROTECTED | PACKAGE_ACCESS | SCRIPT_PRIVATE) && checkDisjoint(pos, flags, PRIVATE, PUBLIC | PROTECTED | PACKAGE_ACCESS | SCRIPT_PRIVATE) && checkDisjoint(pos, flags, SCRIPT_PRIVATE, PRIVATE | PROTECTED | PUBLIC | PACKAGE_ACCESS) && checkDisjoint(pos, flags, PACKAGE_ACCESS, PRIVATE | PROTECTED | PUBLIC | SCRIPT_PRIVATE) && (sym.kind == TYP || checkDisjoint(pos, flags, ABSTRACT | NATIVE, STRICTFP)) && checkDisjointWarn(pos, flags, PUBLIC, PUBLIC_INIT | PUBLIC_READ) && checkDisjointWarn(pos, flags, PUBLIC_INIT, PUBLIC_READ) ) { // skip } return flags & ~illegal; } /* ************************************************************************* * Type Validation **************************************************************************/ /** Validate a type expression. That is, * check that all type arguments of a parametric type are within * their bounds. This must be done in a second phase after type attributon * since a class might have a subclass as type parameter bound. E.g: * * class B<A extends C> { ... } * class C extends B<C> { ... } * * and we can't make sure that the bound is already attributed because * of possible cycles. */ private Validator validator = new Validator(); /** Visitor method: Validate a type expression, if it is not null, catching * and reporting any completion failures. */ void validate(VisageTree tree) { try { if (tree != null) tree.accept(validator); } catch (CompletionFailure ex) { completionError(tree.pos(), ex); } } /** Visitor method: Validate a list of type expressions. */ void validate(List<? extends VisageTree> trees) { for (List<? extends VisageTree> l = trees; l.nonEmpty(); l = l.tail) validate(l.head); } /** A visitor class for type validation. */ class Validator extends VisageTreeScanner { @Override public void visitSelect(VisageSelect tree) { if (tree.type.tag == CLASS) { visitSelectInternal(tree); // Check that this type is either fully parameterized, or // not parameterized at all. if (tree.selected.type.isParameterized() && tree.type.tsym.type.getTypeArguments().nonEmpty()) log.error(tree.pos(), MsgSym.MESSAGE_IMPROPERLY_FORMED_TYPE_PARAM_MISSING); } } public void visitSelectInternal(VisageSelect tree) { if (tree.type.getEnclosingType().tag != CLASS && tree.selected.type.isParameterized()) { // The enclosing type is not a class, so we are // looking at a static member type. However, the // qualifying expression is parameterized. log.error(tree.pos(), MsgSym.MESSAGE_CANNOT_SELECT_STATIC_CLASS_FROM_PARAM_TYPE); } else { // otherwise validate the rest of the expression validate(tree.selected); } } /** Default visitor method: do nothing. */ @Override public void visitTree(VisageTree tree) { } } /* ************************************************************************* * Exception checking **************************************************************************/ /* The following methods treat classes as sets that contain * the class itself and all their subclasses */ /** Is given type a subtype of some of the types in given list? */ boolean subset(Type t, List<Type> ts) { for (List<Type> l = ts; l.nonEmpty(); l = l.tail) if (types.isSubtype(t, l.head)) return true; return false; } /** Is given type a subtype or supertype of * some of the types in given list? */ boolean intersects(Type t, List<Type> ts) { for (List<Type> l = ts; l.nonEmpty(); l = l.tail) if (types.isSubtype(t, l.head) || types.isSubtype(l.head, t)) return true; return false; } /** Add type set to given type list, unless it is a subclass of some class * in the list. */ List<Type> incl(Type t, List<Type> ts) { return subset(t, ts) ? ts : excl(t, ts).prepend(t); } /** Remove type set from type set list. */ List<Type> excl(Type t, List<Type> ts) { if (ts.isEmpty()) { return ts; } else { List<Type> ts1 = excl(t, ts.tail); if (types.isSubtype(ts.head, t)) return ts1; else if (ts1 == ts.tail) return ts; else return ts1.prepend(ts.head); } } /** Form the union of two type set lists. */ List<Type> union(List<Type> ts1, List<Type> ts2) { List<Type> ts = ts1; for (List<Type> l = ts2; l.nonEmpty(); l = l.tail) ts = incl(l.head, ts); return ts; } /** Form the difference of two type lists. */ List<Type> diff(List<Type> ts1, List<Type> ts2) { List<Type> ts = ts1; for (List<Type> l = ts2; l.nonEmpty(); l = l.tail) ts = excl(l.head, ts); return ts; } /** Form the intersection of two type lists. */ public List<Type> intersect(List<Type> ts1, List<Type> ts2) { List<Type> ts = List.nil(); for (List<Type> l = ts1; l.nonEmpty(); l = l.tail) if (subset(l.head, ts2)) ts = incl(l.head, ts); for (List<Type> l = ts2; l.nonEmpty(); l = l.tail) if (subset(l.head, ts1)) ts = incl(l.head, ts); return ts; } /** Is exc an exception symbol that need not be declared? */ boolean isUnchecked(ClassSymbol exc) { return exc.kind == ERR || exc.isSubClass(syms.errorType.tsym, types) || exc.isSubClass(syms.runtimeExceptionType.tsym, types); } /** Is exc an exception type that need not be declared? */ boolean isUnchecked(Type exc) { return (exc.tag == TYPEVAR) ? isUnchecked(types.supertype(exc)) : (exc.tag == CLASS) ? isUnchecked((ClassSymbol)exc.tsym) : exc.tag == BOT; } /** Same, but handling completion failures. */ boolean isUnchecked(DiagnosticPosition pos, Type exc) { try { return isUnchecked(exc); } catch (CompletionFailure ex) { completionError(pos, ex); return true; } } /** Is exc handled by given exception list? */ boolean isHandled(Type exc, List<Type> handled) { return isUnchecked(exc) || subset(exc, handled); } /** Return all exceptions in thrown list that are not in handled list. * @param thrown The list of thrown exceptions. * @param handled The list of handled exceptions. */ List<Type> unHandled(List<Type> thrown, List<Type> handled) { List<Type> unhandled = List.nil(); for (List<Type> l = thrown; l.nonEmpty(); l = l.tail) if (!isHandled(l.head, handled)) unhandled = unhandled.prepend(l.head); return unhandled; } /* ************************************************************************* * Overriding/Implementation checking **************************************************************************/ /** The level of access protection given by a flag set, * where PRIVATE is highest and PUBLIC is lowest. */ static int protection(long flags) { // because the SCRIPT_PRIVATE bit is too high for the switch, test it later switch ((short)(flags & Flags.AccessFlags)) { case PRIVATE: return 3; case PROTECTED: return 1; default: case PUBLIC: return 0; // 'package' vs script-private case 0: return ((flags & SCRIPT_PRIVATE)==0)? 2 : 3; } } /** A string describing the access permission given by a flag set. * This always returns a space-separated list of Java Keywords. */ public static String protectionString(long flags) { long flags1 = flags & (VisageFlags.VisageAccessFlags | VisageFlags.VisageExplicitAccessFlags); return VisageTreeInfo.flagNames(flags1); } /** A customized "cannot override" error message. * @param m The overriding method. * @param other The overridden method. * @return An internationalized string. */ static Object cannotOverride(MethodSymbol m, MethodSymbol other) { String key; if ((other.owner.flags() & INTERFACE) == 0) key = MsgSym.MESSAGE_CANNOT_OVERRIDE; else if ((m.owner.flags() & INTERFACE) == 0) key = MsgSym.MESSAGE_CANNOT_IMPLEMENT; else key = MsgSym.MESSAGE_CLASHES_WITH; return JCDiagnostic.fragment(key, m, m.location(), other, other.location()); } /** A customized "override" warning message. * @param m The overriding method. * @param other The overridden method. * @return An internationalized string. */ static Object uncheckedOverrides(MethodSymbol m, MethodSymbol other) { String key; if ((other.owner.flags() & INTERFACE) == 0) key = MsgSym.MESSAGE_UNCHECKED_OVERRIDE; else if ((m.owner.flags() & INTERFACE) == 0) key = MsgSym.MESSAGE_UNCHECKED_IMPLEMENT; else key = MsgSym.MESSAGE_UNCHECKED_CLASH_WITH; return JCDiagnostic.fragment(key, m, m.location(), other, other.location()); } /** A customized "override" warning message. * @param m The overriding method. * @param other The overridden method. * @return An internationalized string. */ static Object varargsOverrides(MethodSymbol m, MethodSymbol other) { String key; if ((other.owner.flags() & INTERFACE) == 0) key = MsgSym.MESSAGE_VARARGS_OVERRIDE; else if ((m.owner.flags() & INTERFACE) == 0) key = MsgSym.MESSAGE_VARARGS_IMPLEMENT; else key = MsgSym.MESSAGE_VARARGS_CLASH_WITH; return JCDiagnostic.fragment(key, m, m.location(), other, other.location()); } /** Check that this method conforms with overridden method 'other'. * where `origin' is the class where checking started. * Complications: * (1) Do not check overriding of synthetic methods * (reason: they might be final). * todo: check whether this is still necessary. * (2) Admit the case where an interface proxy throws fewer exceptions * than the method it implements. Augment the proxy methods with the * undeclared exceptions in this case. * (3) When generics are enabled, admit the case where an interface proxy * has a result type * extended by the result type of the method it implements. * Change the proxies result type to the smaller type in this case. * * @param tree The tree from which positions * are extracted for errors. * @param m The overriding method. * @param other The overridden method. * @param origin The class of which the overriding method * is a member. */ private void checkOverride(VisageTree tree, MethodSymbol m, MethodSymbol other, ClassSymbol origin) { // Don't check overriding of synthetic methods or by bridge methods. if ((m.flags() & (SYNTHETIC|BRIDGE)) != 0 || (other.flags() & SYNTHETIC) != 0) { return; } // Error if static method overrides instance method (JLS 8.4.6.2). if ((m.flags() & STATIC) != 0 && (other.flags() & STATIC) == 0) { log.error(VisageTreeInfo.diagnosticPositionFor(m, tree), MsgSym.MESSAGE_OVERRIDE_STATIC, cannotOverride(m, other)); return; } // Error if instance method overrides static or final // method (JLS 8.4.6.1). if ((other.flags() & FINAL) != 0 || (m.flags() & STATIC) == 0 && (other.flags() & STATIC) != 0) { log.error(VisageTreeInfo.diagnosticPositionFor(m, tree), MsgSym.MESSAGE_OVERRIDE_METH, cannotOverride(m, other), VisageTreeInfo.flagNames(other.flags() & (FINAL | STATIC))); return; } // Error if bound function overrides non-bound. if ((other.flags() & BOUND) == 0 && (m.flags() & BOUND) != 0) { log.error(VisageTreeInfo.diagnosticPositionFor(m, tree), MsgSym.MESSAGE_VISAGE_BOUND_OVERRIDE_METH, cannotOverride(m, other)); return; } // Error if non-bound function overrides bound. if ((other.flags() & BOUND) != 0 && (m.flags() & BOUND) == 0) { log.error(VisageTreeInfo.diagnosticPositionFor(m, tree), MsgSym.MESSAGE_VISAGE_NON_BOUND_OVERRIDE_METH, cannotOverride(m, other)); return; } if ((m.owner.flags() & ANNOTATION) != 0) { // handled in validateAnnotationMethod return; } // Error if overriding method has weaker access (JLS 8.4.6.3). /*--------------- Taken out. Visage doesn't have the JLS 8.4.6.3 rule... if ((origin.flags() & INTERFACE) == 0 && protection(m.flags()) > protection(other.flags())) { log.error(VisageTreeInfo.diagnosticPositionFor(m, tree), "override.weaker.access", cannotOverride(m, other), protectionString(other.flags())); return; } ----------------- */ Type mt = types.memberType(origin.type, m); Type ot = types.memberType(origin.type, other); // Error if overriding result type is different // (or, in the case of generics mode, not a subtype) of // overridden result type. We have to rename any type parameters // before comparing types. List<Type> mtvars = mt.getTypeArguments(); List<Type> otvars = ot.getTypeArguments(); Type mtres = mt.getReturnType(); Type otres = types.subst(ot.getReturnType(), otvars, mtvars); overrideWarner.warned = false; boolean resultTypesOK = types.returnTypeSubstitutable(mt, ot, otres, overrideWarner); if (!resultTypesOK) { if (!source.allowCovariantReturns() && m.owner != origin && m.owner.isSubClass(other.owner, types)) { // allow limited interoperability with covariant returns } else { typeError(VisageTreeInfo.diagnosticPositionFor(m, tree), JCDiagnostic.fragment(MsgSym.MESSAGE_OVERRIDE_INCOMPATIBLE_RET, cannotOverride(m, other)), mtres, otres); return; } } else if (overrideWarner.warned) { warnUnchecked(VisageTreeInfo.diagnosticPositionFor(m, tree), MsgSym.MESSAGE_PROB_FOUND_REQ, JCDiagnostic.fragment(MsgSym.MESSAGE_OVERRIDE_UNCHECKED_RET, uncheckedOverrides(m, other)), mtres, otres); } // Error if overriding method throws an exception not reported // by overridden method. List<Type> otthrown = types.subst(ot.getThrownTypes(), otvars, mtvars); List<Type> unhandled = unHandled(mt.getThrownTypes(), otthrown); if (unhandled.nonEmpty()) { log.error(VisageTreeInfo.diagnosticPositionFor(m, tree), MsgSym.MESSAGE_OVERRIDE_METH_DOES_NOT_THROW, cannotOverride(m, other), unhandled.head); return; } // Optional warning if varargs don't agree if ((((m.flags() ^ other.flags()) & Flags.VARARGS) != 0) && lint.isEnabled(Lint.LintCategory.OVERRIDES)) { log.warning(VisageTreeInfo.diagnosticPositionFor(m, tree), ((m.flags() & Flags.VARARGS) != 0) ? MsgSym.MESSAGE_OVERRIDE_VARARGS_MISSING : MsgSym.MESSAGE_OVERRIDE_VARARGS_EXTRA, varargsOverrides(m, other)); } // Warn if instance method overrides bridge method (compiler spec ??) if ((other.flags() & BRIDGE) != 0) { log.warning(VisageTreeInfo.diagnosticPositionFor(m, tree), MsgSym.MESSAGE_OVERRIDE_BRIDGE, uncheckedOverrides(m, other)); } // Warn if a deprecated method overridden by a non-deprecated one. if ((other.flags() & DEPRECATED) != 0 && (m.flags() & DEPRECATED) == 0 && m.outermostClass() != other.outermostClass() && !isDeprecatedOverrideIgnorable(other, origin)) { warnDeprecated(VisageTreeInfo.diagnosticPositionFor(m, tree), other); } } // where private boolean isDeprecatedOverrideIgnorable(MethodSymbol m, ClassSymbol origin) { // If the method, m, is defined in an interface, then ignore the issue if the method // is only inherited via a supertype and also implemented in the supertype, // because in that case, we will rediscover the issue when examining the method // in the supertype. // If the method, m, is not defined in an interface, then the only time we need to // address the issue is when the method is the supertype implemementation: any other // case, we will have dealt with when examining the supertype classes ClassSymbol mc = m.enclClass(); Type st = types.supertype(origin.type); if (st.tag != CLASS) return true; MethodSymbol stimpl = types.implementation(m, (ClassSymbol)st.tsym, false); if (mc != null && ((mc.flags() & INTERFACE) != 0)) { List<Type> intfs = types.interfaces(origin.type); return (intfs.contains(mc.type) ? false : (stimpl != null)); } else return (stimpl != m); } // used to check if there were any unchecked conversions private Warner overrideWarner = new Warner(); /** Check that a class does not inherit two concrete methods * with the same signature. * @param pos Position to be used for error reporting. * @param site The class type to be checked. */ private void checkCompatibleConcretes(DiagnosticPosition pos, Type site) { Type sup = types.supertype(site); if (sup.tag != CLASS) return; for (Type t1 = sup; t1.tsym.type.isParameterized(); t1 = types.supertype(t1)) { for (Scope.Entry e1 = t1.tsym.members().elems; e1 != null; e1 = e1.sibling) { Symbol s1 = e1.sym; if (s1.kind != MTH || (s1.flags() & (STATIC|SYNTHETIC|BRIDGE)) != 0 || !s1.isInheritedIn(site.tsym, types) || types.implementation((MethodSymbol)s1, site.tsym, true) != s1) continue; Type st1 = types.memberType(t1, s1); int s1ArgsLength = st1.getParameterTypes().length(); if (st1 == s1.type) continue; for (Type t2 = sup; t2.tag == CLASS; t2 = types.supertype(t2)) { for (Scope.Entry e2 = t1.tsym.members().lookup(s1.name); e2.scope != null; e2 = e2.next()) { Symbol s2 = e2.sym; if (s2 == s1 || s2.kind != MTH || (s2.flags() & (STATIC|SYNTHETIC|BRIDGE)) != 0 || s2.type.getParameterTypes().length() != s1ArgsLength || !s2.isInheritedIn(site.tsym, types) || types.implementation((MethodSymbol)s2, site.tsym, true) != s2) continue; Type st2 = types.memberType(t2, s2); if (types.overrideEquivalent(st1, st2)) log.error(pos, MsgSym.MESSAGE_CONCRETE_INHERITANCE_CONFLICT, s1, t1, s2, t2, sup); } } } } } /** Check that classes (or interfaces) do not each define an abstract * method with same name and arguments but incompatible return types. * @param pos Position to be used for error reporting. * @param t1 The first argument type. * @param t2 The second argument type. */ private boolean checkCompatibleAbstracts(DiagnosticPosition pos, Type t1, Type t2, Type site) { Symbol sym = firstIncompatibility(t1, t2, site); if (sym != null) { if (sym.kind == VAR) { log.error(pos, MsgSym.MESSAGE_VISAGE_TYPES_INCOMPATIBLE_VARS, t1, t2, sym.name); } else { log.error(pos, MsgSym.MESSAGE_TYPES_INCOMPATIBLE_DIFF_RET, t1, t2, sym.name + "(" + types.memberType(t2, sym).getParameterTypes() + ")"); } return false; } return true; } /** Return the first method which is defined with same args * but different return types in two given interfaces, or null if none * exists. * @param t1 The first type. * @param t2 The second type. * @param site The most derived type. * @returns symbol from t2 that conflicts with one in t1. */ private Symbol firstIncompatibility(Type t1, Type t2, Type site) { Map<TypeSymbol,Type> interfaces1 = new HashMap<TypeSymbol,Type>(); closure(t1, interfaces1); Map<TypeSymbol,Type> interfaces2; if (t1 == t2) interfaces2 = interfaces1; else closure(t2, interfaces1, interfaces2 = new HashMap<TypeSymbol,Type>()); for (Type t3 : interfaces1.values()) { for (Type t4 : interfaces2.values()) { Symbol s = firstDirectIncompatibility(t3, t4, site); if (s != null) return s; } } return null; } /** Compute all the supertypes of t, indexed by type symbol. */ private void closure(Type t, Map<TypeSymbol,Type> typeMap) { if (t.tag != CLASS) return; if (typeMap.put(t.tsym, t) == null) { closure(types.supertype(t), typeMap); for (Type i : types.interfaces(t)) closure(i, typeMap); } } /** Compute all the supertypes of t, indexed by type symbol (except thise in typesSkip). */ private void closure(Type t, Map<TypeSymbol,Type> typesSkip, Map<TypeSymbol,Type> typeMap) { if (t.tag != CLASS) return; if (typesSkip.get(t.tsym) != null) return; if (typeMap.put(t.tsym, t) == null) { closure(types.supertype(t), typesSkip, typeMap); for (Type i : types.interfaces(t)) closure(i, typesSkip, typeMap); } } /** Return the first method in t2 that conflicts with a method from t1. */ private Symbol firstDirectIncompatibility(Type t1, Type t2, Type site) { Symbol s = firstDirectMethodIncompatibility(t1, t2, site); if (s != null) { return s; } else { return firstDirectVarIncompatibility(t1, t2, site); } } private Symbol firstDirectMethodIncompatibility(Type t1, Type t2, Type site) { for (Scope.Entry e1 = t1.tsym.members().elems; e1 != null; e1 = e1.sibling) { Symbol s1 = e1.sym; s1.complete(); Type st1 = null; if (s1.kind != MTH || s1.name == defs.internalRunFunctionName || !s1.isInheritedIn(site.tsym, types)) continue; for (Scope.Entry e2 = t2.tsym.members().lookup(s1.name); e2.scope != null; e2 = e2.next()) { Symbol s2 = e2.sym; s2.complete(); if (s1 == s2) continue; if (s2.kind != MTH || !s2.isInheritedIn(site.tsym, types)) continue; if (st1 == null) st1 = types.memberType(t1, s1); Type st2 = types.memberType(t2, s2); if (types.overrideEquivalent(st1, st2)) { List<Type> tvars1 = st1.getTypeArguments(); List<Type> tvars2 = st2.getTypeArguments(); Type rt1 = st1.getReturnType(); Type rt2 = types.subst(st2.getReturnType(), tvars2, tvars1); boolean compat = types.isSameType(rt1, rt2) || rt1.tag >= CLASS && rt2.tag >= CLASS && (types.covariantReturnType(rt1, rt2, Warner.noWarnings) || types.covariantReturnType(rt2, rt1, Warner.noWarnings)); if (!compat) return s2; } } } return null; } private Symbol firstDirectVarIncompatibility(Type t1, Type t2, Type site) { for (Scope.Entry e1 = t1.tsym.members().elems; e1 != null; e1 = e1.sibling) { Symbol s1 = e1.sym; s1.complete(); Type st1 = null; if (s1.kind != VAR || !s1.isInheritedIn(site.tsym, types)) continue; for (Scope.Entry e2 = t2.tsym.members().lookup(s1.name); e2.scope != null; e2 = e2.next()) { Symbol s2 = e2.sym; s2.complete(); if (s1 == s2) continue; if (s2.kind != VAR || !s2.isInheritedIn(site.tsym, types)) continue; if (!types.isSameType(s1.type, s2.type)) { return s2; } } } return null; } /** Check that a given method conforms with any method it overrides. * @param tree The tree from which positions are extracted * for errors. * @param m The overriding method. */ void checkOverride(VisageTree tree, MethodSymbol m) { ClassSymbol origin = (ClassSymbol) m.owner; boolean doesOverride = false; if ((origin.flags() & ENUM) != 0 && names.finalize.equals(m.name)) { if (m.overrides(syms.enumFinalFinalize, origin, types, false)) { log.error(tree.pos(), MsgSym.MESSAGE_ENUM_NO_FINALIZE); return; } } for (Type t : types.supertypesClosure(origin.type)) { if (t.tag == CLASS) { TypeSymbol c = t.tsym; Scope.Entry e = c.members().lookup(m.name); while (e.scope != null) { e.sym.complete(); if (types.overrides(m, e.sym, origin, false)) { checkOverride(tree, m, (MethodSymbol)e.sym, origin); doesOverride = !e.sym.type.getReturnType().isErroneous(); } e = e.next(); } } } boolean declaredOverride = (m.flags() & OVERRIDE) != 0; if (doesOverride) { if (!declaredOverride && (m.flags() & (Flags.SYNTHETIC|Flags.STATIC)) == 0) { log.warning(tree.pos(), MsgSym.MESSAGE_VISAGE_SHOULD_BE_DECLARED_OVERRIDE, m); } } else { if (declaredOverride) { log.error(tree.pos(), MsgSym.MESSAGE_VISAGE_DECLARED_OVERRIDE_DOES_NOT, rs.kindName(m), m); } } } /** Check to make sure that any mixins don't create var conflicts. */ void checkMixinConflicts(VisageClassDeclaration tree) { for (VisageExpression mixin : tree.getMixing()) { if (mixin instanceof VisageIdent) { Symbol symbol = ((VisageIdent)mixin).sym; if ((symbol.flags_field & VisageFlags.MIXIN) != 0) { ClassSymbol mixinSym = (ClassSymbol)symbol; Scope s = mixinSym.members(); for (Scope.Entry e = s.elems; e != null; e = e.sibling) { if (e.sym.kind == VAR) { checkVarOverride(mixin.pos(), (VisageVarSymbol)e.sym, tree.sym, false); } } } } } } /** Check that var/def does not override (unless it is hidden by being script private) */ void checkVarOverride(DiagnosticPosition diagPos, VisageVarSymbol vsym) { checkVarOverride(diagPos, vsym, (ClassSymbol)vsym.owner, true); } void checkVarOverride(DiagnosticPosition diagPos, VisageVarSymbol vsym, ClassSymbol origin, boolean overrides) { for (Type t : types.supertypesClosure(origin.type)) { if (t.tag == CLASS) { TypeSymbol c = t.tsym; for (Scope.Entry e = c.members().lookup(vsym.name); e.scope != null; e = e.next()) { Symbol eSym = e.sym; eSym.complete(); if (!(eSym.owner instanceof ClassSymbol)) continue; long flags = eSym.flags_field; boolean isNotScriptPrivate = (flags & VisageFlags.SCRIPT_PRIVATE) == 0L; boolean isPublicRead = (flags & (VisageFlags.PUBLIC_READ|VisageFlags.PUBLIC_INIT)) != 0L; boolean isScriptScope = origin.outermostClass() == ((ClassSymbol) eSym.owner).outermostClass(); if (isNotScriptPrivate || isPublicRead || isScriptScope) { // We have a name clash, the variable name is the name of a member // which is visible outside the script or which is in the same script if (!types.isVisageClass(eSym.owner)) { log.error(diagPos, (vsym.flags_field & VisageFlags.IS_DEF) == 0L? MsgSym.MESSAGE_VISAGE_VAR_OVERRIDES_JAVA_MEMBER : MsgSym.MESSAGE_VISAGE_DEF_OVERRIDES_JAVA_MEMBER, eSym, eSym.owner); } else if (overrides) { log.error(diagPos, (vsym.flags_field & VisageFlags.IS_DEF) == 0L? MsgSym.MESSAGE_VISAGE_VAR_OVERRIDES_MEMBER : MsgSym.MESSAGE_VISAGE_DEF_OVERRIDES_MEMBER, eSym, eSym.owner); } return; } } } } } /** Check that all abstract members of given class have definitions. * @param pos Position to be used for error reporting. * @param c The class. */ void checkAllDefined(DiagnosticPosition pos, ClassSymbol c) { try { MethodSymbol undef = firstUndef(c, c); if (undef != null) { if ((c.flags() & ENUM) != 0 && types.supertype(c.type).tsym == syms.enumSym && (c.flags() & FINAL) == 0) { // add the ABSTRACT flag to an enum c.flags_field |= ABSTRACT; } else { MethodSymbol undef1 = new MethodSymbol(undef.flags(), undef.name, types.memberType(c.type, undef), undef.owner); log.error(pos, MsgSym.MESSAGE_DOES_NOT_OVERRIDE_ABSTRACT, c, undef1, undef1.location()); } } } catch (CompletionFailure ex) { completionError(pos, ex); } } //where /** Return first abstract member of class `c' that is not defined * in `impl', null if there is none. */ private MethodSymbol firstUndef(ClassSymbol impl, ClassSymbol c) { MethodSymbol undef = null; // Do not bother to search in classes that are not abstract, // since they cannot have abstract members. if (c == impl || (c.flags() & (ABSTRACT | INTERFACE | MIXIN)) != 0) { Scope s = c.members(); for (Scope.Entry e = s.elems; undef == null && e != null; e = e.sibling) { if (e.sym.kind == MTH && (e.sym.flags() & (ABSTRACT|IPROXY)) == ABSTRACT) { MethodSymbol absmeth = (MethodSymbol)e.sym; MethodSymbol implmeth = types.implementation(absmeth, impl, true); if (implmeth == null || implmeth == absmeth) { undef = absmeth; } } } if (undef == null) { Type st = types.supertype(c.type); if (st.tag == CLASS) undef = firstUndef(impl, (ClassSymbol)st.tsym); } for (List<Type> l = types.interfaces(c.type); undef == null && l.nonEmpty(); l = l.tail) { undef = firstUndef(impl, (ClassSymbol)l.head.tsym); } } return undef; } /** Check for cyclic references. Issue an error if the * symbol of the type referred to has a LOCKED flag set. * * @param pos Position to be used for error reporting. * @param t The type referred to. */ void checkNonCyclic(DiagnosticPosition pos, Type t) { checkNonCyclicInternal(pos, t, false); } void checkNonCyclic(DiagnosticPosition pos, TypeVar t) { checkNonCyclic1(pos, t, new HashSet<TypeVar>()); } private void checkNonCyclic1(DiagnosticPosition pos, Type t, Set<TypeVar> seen) { final TypeVar tv; if (seen.contains(t)) { tv = (TypeVar)t; tv.bound = new ErrorType(); log.error(pos, MsgSym.MESSAGE_CYCLIC_INHERITANCE, t); } else if (t.tag == TYPEVAR) { tv = (TypeVar)t; seen.add(tv); for (Type b : types.getBounds(tv)) checkNonCyclic1(pos, b, seen); } } /** Check for cyclic references. Issue an error if the * symbol of the type referred to has a LOCKED flag set. * * @param pos Position to be used for error reporting. * @param t The type referred to. * @returns True if the check completed on all attributed classes */ private boolean checkNonCyclicInternal(DiagnosticPosition pos, Type t, boolean ownerCycle) { boolean complete = true; // was the check complete? //- System.err.println("checkNonCyclicInternal("+t+");");//DEBUG Symbol c = t.tsym; if ((c.flags_field & ACYCLIC) != 0) return true; if ((c.flags_field & LOCKED) != 0) { noteCyclic(pos, (ClassSymbol)c, ownerCycle); } else if (!c.type.isErroneous()) { try { c.flags_field |= LOCKED; if (c.type.tag == CLASS) { ClassType clazz = (ClassType)c.type; if (clazz.interfaces_field != null) for (List<Type> l=clazz.interfaces_field; l.nonEmpty(); l=l.tail) complete &= checkNonCyclicInternal(pos, l.head, ownerCycle); if (clazz.supertype_field != null) { Type st = clazz.supertype_field; if (st != null && st.tag == CLASS) complete &= checkNonCyclicInternal(pos, st, ownerCycle); } if (c.owner.kind == TYP) complete &= checkNonCyclicInternal(pos, c.owner.type, true); } } finally { c.flags_field &= ~LOCKED; } } if (complete) complete = ((c.flags_field & UNATTRIBUTED) == 0) && c.completer == null; if (complete) c.flags_field |= ACYCLIC; return complete; } /** Note that we found an inheritance cycle. */ private void noteCyclic(DiagnosticPosition pos, ClassSymbol c, boolean ownerCycle) { if (!ownerCycle) log.error(pos, MsgSym.MESSAGE_CYCLIC_INHERITANCE, c); else log.error(pos, MsgSym.MESSAGE_CANNOT_INHERIT_FROM_SCRIPT_CLASS, c); for (List<Type> l=types.interfaces(c.type); l.nonEmpty(); l=l.tail) l.head = new ErrorType((ClassSymbol)l.head.tsym); Type st = types.supertype(c.type); if (st.tag == CLASS) ((ClassType)c.type).supertype_field = new ErrorType((ClassSymbol)st.tsym); c.type = new ErrorType(c); c.flags_field |= ACYCLIC; } /** Check that all methods which implement some * method conform to the method they implement. * @param tree The class definition whose members are checked. */ void checkImplementations(VisageClassDeclaration tree) { checkImplementations(tree, tree.sym); } //where /** Check that all methods which implement some * method in `ic' conform to the method they implement. */ void checkImplementations(VisageClassDeclaration tree, ClassSymbol ic) { ClassSymbol origin = tree.sym; for (List<Type> l = types.closure(ic.type); l.nonEmpty(); l = l.tail) { ClassSymbol lc = (ClassSymbol)l.head.tsym; if ((allowGenerics || origin != lc) && (lc.flags() & (ABSTRACT|MIXIN)) != 0) { for (Scope.Entry e=lc.members().elems; e != null; e=e.sibling) { if (e.sym.kind == MTH && (e.sym.flags() & (STATIC|ABSTRACT)) == ABSTRACT) { MethodSymbol absmeth = (MethodSymbol)e.sym; MethodSymbol implmeth = types.implementation(absmeth, origin, false); if (implmeth != null && implmeth != absmeth && (implmeth.owner.flags() & INTERFACE) == (origin.flags() & INTERFACE)) { // don't check if implmeth is in a class, yet // origin is an interface. This case arises only // if implmeth is declared in Object. The reason is // that interfaces really don't inherit from // Object it's just that the compiler represents // things that way. checkOverride(tree, implmeth, absmeth, origin); } } } } } } /** Check that only one extend class is a java or visage base class. * @param tree The class definition whose extends are checked. **/ void checkOneBaseClass(VisageClassDeclaration tree) { // Get the list of non-mixin extends. List<VisageExpression> extending = tree.getExtending(); // If there is more than one then we have too many. if (extending.size() > 1) { // Get the first extra for error position. VisageExpression extra = extending.get(1); log.error(extra.pos(), MsgSym.MESSAGE_VISAGE_ONLY_ONE_BASE_CLASS_ALLOWED); } } /** Check that a mixin class is pure of other modifiers. * @param pos Position to be used for error reporting. * @param c The class whose modifiers are checked. **/ void checkPureMixinClass(DiagnosticPosition pos, ClassSymbol c) { if ((c.flags() & ABSTRACT) != 0) { log.error(pos, MsgSym.MESSAGE_VISAGE_PURE_MIXIN); } } /** Check that a mixin class has only mixin extends. * @param tree The class definition whose extends are checked. **/ void checkOnlyMixinsAndInterfaces(VisageClassDeclaration tree) { // Get the list of non-mixin extends. List<VisageExpression> extending = tree.getExtending(); // Any is too many. if (extending.size() > 0) { // Get the first extra for error position. VisageExpression extra = extending.get(0); log.error(extra.pos(), MsgSym.MESSAGE_VISAGE_ONLY_MIXINS_AND_INTERFACES); } } /** Check that all abstract methods implemented by a class are * mutually compatible. * @param pos Position to be used for error reporting. * @param c The class whose interfaces are checked. */ void checkCompatibleSupertypes(DiagnosticPosition pos, Type c) { List<Type> supertypes = types.interfaces(c); Type supertype = types.supertype(c); if (supertype.tag == CLASS && (supertype.tsym.flags() & (ABSTRACT|MIXIN)) != 0) { supertypes = supertypes.prepend(supertype); } for (List<Type> l = supertypes; l.nonEmpty(); l = l.tail) { if (allowGenerics && !l.head.getTypeArguments().isEmpty() && !checkCompatibleAbstracts(pos, l.head, l.head, c)) { return; } for (List<Type> m = supertypes; m != l; m = m.tail) { if (!checkCompatibleAbstracts(pos, l.head, m.head, c)) { return; } } } checkCompatibleConcretes(pos, c); } /** Check that class c does not implement directly or indirectly * the same parameterized interface with two different argument lists. * @param pos Position to be used for error reporting. * @param type The type whose interfaces are checked. */ void checkClassBounds(DiagnosticPosition pos, Type type) { checkClassBounds(pos, new HashMap<TypeSymbol,Type>(), type); } //where /** Enter all interfaces of type `type' into the hash table `seensofar' * with their class symbol as key and their type as value. Make * sure no class is entered with two different types. */ void checkClassBounds(DiagnosticPosition pos, Map<TypeSymbol,Type> seensofar, Type type) { if (type.isErroneous()) return; for (List<Type> l = types.interfaces(type); l.nonEmpty(); l = l.tail) { Type it = l.head; Type oldit = seensofar.put(it.tsym, it); if (oldit != null) { List<Type> oldparams = oldit.allparams(); List<Type> newparams = it.allparams(); if (!types.containsTypeEquivalent(oldparams, newparams)) log.error(pos, MsgSym.MESSAGE_CANNOT_INHERIT_DIFF_ARG, it.tsym, Type.toString(oldparams), Type.toString(newparams)); } checkClassBounds(pos, seensofar, it); } Type st = types.supertype(type); if (st != null) checkClassBounds(pos, seensofar, st); } /** Enter interface or mixin into into set. * If the class is a duplicate, issue a "repeated interface/mixin" error. */ void checkNotRepeated(DiagnosticPosition pos, Type it, Set<Type> its) { // if class is already in the set. if (its.contains(it)) { // If class is a mixin. if ((it.tsym.flags_field & VisageFlags.MIXIN) != 0) log.error(pos, MsgSym.MESSAGE_VISAGE_REPEATED_MIXIN); else log.error(pos, MsgSym.MESSAGE_REPEATED_INTERFACE); } else { its.add(it); } } /* ************************************************************************* * Miscellaneous **************************************************************************/ /** * Return the opcode of the operator but emit an error if it is an * error. * @param pos position for error reporting. * @param operator an operator * @param tag a tree tag * @param left type of left hand side * @param right type of right hand side */ int checkOperator(DiagnosticPosition pos, OperatorSymbol operator, VisageTag tag, Type left, Type right) { if (operator.opcode == ByteCodes.error) { log.error(pos, MsgSym.MESSAGE_OPERATOR_CANNOT_BE_APPLIED, treeinfo.operatorName(tag), left + "," + right); } return operator.opcode; } /** * Check for division by integer constant zero * @param pos Position for error reporting. * @param operator The operator for the expression * @param operand The right hand operand for the expression */ void checkDivZero(DiagnosticPosition pos, Symbol operator, Type operand) { if (operand.constValue() != null && lint.isEnabled(Lint.LintCategory.DIVZERO) && operand.tag <= LONG && ((Number) (operand.constValue())).longValue() == 0) { int opc = ((OperatorSymbol)operator).opcode; if (opc == ByteCodes.idiv || opc == ByteCodes.imod || opc == ByteCodes.ldiv || opc == ByteCodes.lmod) { log.warning(pos, MsgSym.MESSAGE_DIV_ZERO); } } } /** Check that symbol is unique in given scope. * @param pos Position for error reporting. * @param sym The symbol. * @param s The scope. */ boolean checkUnique(DiagnosticPosition pos, Symbol sym, VisageEnv<VisageAttrContext> env) { boolean shouldContinue = true; do { shouldContinue = !attr.isClassOrFuncDef(env, false); if (!checkUnique(pos, sym, VisageEnter.enterScope(env))) return false; env = env.outer; } while (env != null && shouldContinue); return true; } boolean checkUnique(DiagnosticPosition pos, Symbol sym, Scope s) { if (sym.type != null && sym.type.isErroneous()) { return true; } if (sym.owner.name == names.any) { return false; } for (Scope.Entry e = s.lookup(sym.name); e.scope == s; e = e.next()) { sym.complete(); if (sym != e.sym && sym.kind == e.sym.kind && sym.name != names.error && (sym.kind != MTH || types.overrideEquivalent(sym.type, e.sym.type))) { if ((sym.flags() & VARARGS) != (e.sym.flags() & VARARGS)) { varargsDuplicateError(pos, sym, e.sym); } else { duplicateError(pos, e.sym); } return false; } } return true; } /** Check that single-type import is not already imported or top-level defined, * but make an exception for two single-type imports which denote the same type. * @param pos Position for error reporting. * @param sym The symbol. * @param s The scope */ boolean checkUniqueImport(DiagnosticPosition pos, Symbol sym, Scope s) { return checkUniqueImport(pos, sym, s, false); } /** Check that static single-type import is not already imported or top-level defined, * but make an exception for two single-type imports which denote the same type. * @param pos Position for error reporting. * @param sym The symbol. * @param s The scope * @param staticImport Whether or not this was a static import */ boolean checkUniqueStaticImport(DiagnosticPosition pos, Symbol sym, Scope s) { return checkUniqueImport(pos, sym, s, true); } /** Check that single-type import is not already imported or top-level defined, * but make an exception for two single-type imports which denote the same type. * @param pos Position for error reporting. * @param sym The symbol. * @param s The scope. * @param staticImport Whether or not this was a static import */ private boolean checkUniqueImport(DiagnosticPosition pos, Symbol sym, Scope s, boolean staticImport) { for (Scope.Entry e = s.lookup(sym.name); e.scope != null; e = e.next()) { // is encountered class entered via a class declaration? boolean isClassDecl = e.scope == s; if ((isClassDecl || sym != e.sym) && sym.kind == e.sym.kind && sym.name != names.error) { if (!e.sym.type.isErroneous()) { String what = e.sym.toString(); if (!isClassDecl) { if (staticImport) log.error(pos, MsgSym.MESSAGE_ALREADY_DEFINED_STATIC_SINGLE_IMPORT, what); else log.error(pos, MsgSym.MESSAGE_ALREADY_DEFINED_SINGLE_IMPORT, what); } else if (sym != e.sym) log.error(pos, MsgSym.MESSAGE_ALREADY_DEFINED_THIS_UNIT, what); } return false; } } return true; } /** Check that a qualified name is in canonical form (for import decls). */ public void checkCanonical(VisageTree tree) { if (!isCanonical(tree)) log.error(tree.pos(), MsgSym.MESSAGE_IMPORT_REQUIRES_CANONICAL, VisageTreeInfo.symbol(tree)); } // where private boolean isCanonical(VisageTree tree) { while (tree.getVisageTag() == VisageTag.SELECT) { VisageSelect s = (VisageSelect) tree; if (s.sym.owner != VisageTreeInfo.symbol(s.selected)) return false; tree = s.selected; } return true; } private class ConversionWarner extends Warner { final String key; final Type found; final Type expected; public ConversionWarner(DiagnosticPosition pos, String key, Type found, Type expected) { super(pos); this.key = key; this.found = found; this.expected = expected; } @Override public void warnUnchecked() { boolean localWarned = this.warned; super.warnUnchecked(); if (localWarned) return; // suppress redundant diagnostics Object problem = JCDiagnostic.fragment(key); VisageCheck.this.warnUnchecked(pos(), MsgSym.MESSAGE_PROB_FOUND_REQ, problem, found, expected); } } public Warner castWarner(DiagnosticPosition pos, Type found, Type expected) { return new ConversionWarner(pos, MsgSym.MESSAGE_UNCHECKED_CAST_TO_TYPE, found, expected); } public Warner convertWarner(DiagnosticPosition pos, Type found, Type expected) { return new ConversionWarner(pos, MsgSym.MESSAGE_UNCHECKED_ASSIGN, found, expected); } public void warnEmptyRangeLiteral(DiagnosticPosition pos, VisageLiteral lower, VisageLiteral upper, VisageLiteral step, boolean isExclusive) { double lowerValue = ((Number)lower.getValue()).doubleValue(); double upperValue = ((Number)upper.getValue()).doubleValue(); double stepValue = step != null? ((Number)step.getValue()).doubleValue() : 1; if ((stepValue > 0 && lowerValue > upperValue) || (stepValue < 0 && lowerValue < upperValue) || (isExclusive && lowerValue == upperValue)) { log.warning(pos, MsgSym.MESSAGE_VISAGE_RANGE_LITERAL_EMPTY); } } public Type checkFunctionType(DiagnosticPosition pos, MethodType m) { if (m.argtypes.length() > VisageSymtab.MAX_FIXED_PARAM_LENGTH) { log.error(pos, MsgSym.MESSAGE_TOO_MANY_PARAMETERS); return syms.errType; } else { return syms.makeFunctionType(m); } } public void checkForwardReferences(VisageTree tree) { final boolean onlyWarnings = options.get("fwdRefError") != null && options.get("fwdRefError").contains("false"); new ForwardReferenceChecker(names, types, defs, getForwardRefKinds()) { @Override protected void reportForwardReference(DiagnosticPosition pos, boolean selfReference, Symbol s, boolean potential) { JCDiagnostic description = diags.fragment(selfReference ? MsgSym.MESSAGE_VISAGE_SELF_REFERENCE : MsgSym.MESSAGE_VISAGE_FORWARD_REFERENCE); if (potential) { log.warning(pos, MsgSym.MESSAGE_MAYBE_FORWARD_REF, description, rs.kindName(VAR), s); } else if (onlyWarnings) { log.warning(pos, MsgSym.MESSAGE_ILLEGAL_FORWARD_REF, description, rs.kindName(VAR), s); } else { log.error(pos, MsgSym.MESSAGE_ILLEGAL_FORWARD_REF, description, rs.kindName(VAR), s); } } }.scan(tree); } public abstract static class ForwardReferenceChecker extends VisageTreeScanner { protected ForwardReferenceChecker(Name.Table names, VisageTypes types, VisageDefs defs, Collection<ScopeKind> kinds) { this.names = names; this.types = types; this.defs = defs; this.optionalKinds = kinds; } Name.Table names; Types types; VisageDefs defs; List<VarScope> scopes = List.nil(); ClassSymbol enclClass = null; Collection<ScopeKind> optionalKinds; public enum ScopeKind { CLASS(false, false), FUNCTION_DEF(false, false), VAR_DEF(false, true), BLOCK_EXPR(false, true), ON_REPLACE(true, false), ON_INVALIDATE(true, false), FUNCTION_VALUE(true, false), INTERPOLATE_VALUE(true, false), KEYFRAME_LIT(true, false), BOUND_CTX(true, false), ASSIGN_CTX(true, false), OBJ_LIT(true, false), OBJ_LIT_FUNC(true, false); boolean optional; boolean defaultTransparent; ScopeKind(boolean optional, boolean defaultTransparent) { this.optional = optional; this.defaultTransparent = defaultTransparent; } public boolean isOptional() { return optional; } public boolean isDefaultTransparent() { return defaultTransparent; } } protected class VarScope { VarScope(ScopeKind kind) { this.kind = kind; } VarScope(ScopeKind kind, VarScope prevScope) { this(kind); this.prevScope = prevScope; } ScopeKind kind; boolean isStatic = false; VarScope prevScope = null; Set<VisageVarSymbol> uninited_vars = new LinkedHashSet<VisageVarSymbol>(); VisageVarSymbol currentVar = null; int overrideVarIdx = Integer.MAX_VALUE; } @Override public void visitClassDeclaration(VisageClassDeclaration tree) { ClassSymbol prevClass = enclClass; try { enclClass = tree.sym; beginScope(isObjLiteral(tree.sym) ? ScopeKind.OBJ_LIT : ScopeKind.CLASS ); addVars(tree.getMembers()); currentScope().isStatic = tree.sym.isStatic(); super.visitClassDeclaration(tree); endScope(); } finally { enclClass = prevClass; } } @Override public void visitFunctionValue(VisageFunctionValue tree) { boolean isLambda = tree.definition.sym.name.equals(defs.lambda_MethodName); beginScope(isLambda ? ScopeKind.FUNCTION_VALUE : isObjLiteral(tree.definition.sym.owner) ? ScopeKind.OBJ_LIT_FUNC : ScopeKind.FUNCTION_DEF); if (!isLambda) { currentScope().isStatic = tree.definition.sym.isStatic(); } super.visitFunctionValue(tree); endScope(); } @Override public void visitOnReplace(VisageOnReplace tree) { beginScope(tree.getTriggerKind() == VisageOnReplace.Kind.ONREPLACE ? ScopeKind.ON_REPLACE : ScopeKind.ON_INVALIDATE); super.visitOnReplace(tree); endScope(); } @Override public void visitKeyFrameLiteral(VisageKeyFrameLiteral tree) { beginScope(ScopeKind.KEYFRAME_LIT); super.visitKeyFrameLiteral(tree); endScope(); } @Override public void visitInterpolateValue(VisageInterpolateValue tree) { beginScope(ScopeKind.INTERPOLATE_VALUE); super.visitInterpolateValue(tree); endScope(); } @Override public void visitInitDefinition(VisageInitDefinition tree) { beginScope(ScopeKind.FUNCTION_DEF); super.visitInitDefinition(tree); endScope(); } @Override public void visitPostInitDefinition(VisagePostInitDefinition tree) { beginScope(ScopeKind.FUNCTION_DEF); super.visitPostInitDefinition(tree); endScope(); } @Override public void visitBlockExpression(VisageBlock tree) { beginScope(ScopeKind.BLOCK_EXPR); addVars(tree.stats); addVar(tree.value); super.visitBlockExpression(tree); endScope(); } @Override public void visitVar(VisageVar tree) { if (tree.getSymbol() != null) { beginScope(ScopeKind.VAR_DEF); currentScope().isStatic = tree.getSymbol().isStatic(); currentScope().currentVar = tree.getSymbol(); super.scan(tree.getInitializer()); removeVar(tree.getSymbol()); super.scan(tree.getOnReplace()); super.scan(tree.getOnInvalidate()); endScope(); } } @Override public void visitAssign(VisageAssign tree) { Symbol s = VisageTreeInfo.symbolFor(tree.lhs); VisageVarSymbol vsym = (s != null && s.kind == VAR) ? (VisageVarSymbol)s : null; if (vsym != null) { beginScope(ScopeKind.ASSIGN_CTX); scan(tree.lhs); endScope(); scan(tree.rhs); } else { super.visitAssign(tree); } } @Override public void visitOverrideClassVar(VisageOverrideClassVar tree) { if (tree.getSymbol() != null) { beginScope(ScopeKind.VAR_DEF); currentScope().overrideVarIdx = tree.getSymbol().getAbsoluteIndex(enclClass.type); currentScope().currentVar = tree.getSymbol(); currentScope().isStatic = tree.getSymbol().isStatic(); scan(tree.getInitializer()); currentScope().overrideVarIdx++; super.scan(tree.getOnReplace()); super.scan(tree.getOnInvalidate()); endScope(); } } @Override public void visitIdent(VisageIdent tree) { checkForwardReference(tree, tree.sym); } @Override public void visitSelect(VisageSelect tree) { Symbol selectedSym = VisageTreeInfo.symbolFor(tree.selected); boolean shouldCheck = false; if (selectedSym != null) { if (selectedSym.kind == TYP) { shouldCheck = true; } if (selectedSym.kind == VAR) { if (selectedSym.name == names._this || selectedSym.name == names._super) { shouldCheck = true; } } if (shouldCheck) { checkForwardReference(tree, tree.sym); } } super.visitSelect(tree); } @Override public void visitVarInit(VisageVarInit tree) { removeVar(tree.getSymbol()); } private void checkForwardReference(VisageExpression tree, Symbol s) { checkForwardReference(currentScope(), tree, s, false); } private void checkForwardReference(VarScope scope, VisageExpression tree, Symbol s, boolean optional) { if ((!tree.isBound() || optionalKinds.contains(ScopeKind.BOUND_CTX)) && s != null && s instanceof VisageVarSymbol) { VisageVarSymbol vsym = (VisageVarSymbol)s; if (scope.currentVar == s) { reportForwardReference(tree, true, vsym, tree.isBound() || optional); } else if (vsym.name != names._this && vsym.name != names._super && (isForwardReferenceInSameClass(scope, vsym) || isForwardReferenceInSubclass(scope, vsym))) { reportForwardReference(tree, false, vsym, tree.isBound() || optional); } else if (isTransparent(scope.kind) && scope.prevScope != null) { checkForwardReference(scope.prevScope, tree, s, optional || scope.kind.isOptional()); } } } //where private boolean isForwardReferenceInSameClass(VarScope scope, VisageVarSymbol vsym) { return scope.uninited_vars.contains(vsym) && vsym.isStatic() == currentScope().isStatic; } //where private boolean isForwardReferenceInSubclass(VarScope scope, VisageVarSymbol vsym) { int refIdx = vsym.getAbsoluteIndex(enclClass.type); return scope.overrideVarIdx <= refIdx && vsym.isMember() && enclClass.isSubClass(vsym.owner, types); } private void beginScope(ScopeKind kind) { VarScope prevScope = currentScope(); VarScope newScope = isTransparent(kind) ? new VarScope(kind, prevScope) : new VarScope(kind); newScope.isStatic = prevScope != null ? prevScope.isStatic : false; scopes = scopes.prepend(newScope); } private void endScope() { scopes = scopes.tail; } protected VarScope currentScope() { return scopes.head; } private void removeVar(VisageVarSymbol vsym) { VarScope scope = currentScope(); while (scope != null) { scope.uninited_vars.remove(vsym); scope = scope.prevScope; } } private void addVars(List<? extends VisageTree> trees) { for (VisageTree t : trees) { addVar(t); } } private void addVar(VisageTree tree) { if (tree != null) { VisageVarSymbol sym = null; switch (tree.getVisageTag()) { case VAR_DEF: sym = ((VisageVar)tree).getSymbol(); break; case VAR_SCRIPT_INIT: sym = ((VisageVarInit)tree).getSymbol(); break; } if (sym != null) { currentScope().uninited_vars.add(sym); } } } private boolean isObjLiteral(Symbol sym) { return sym.name.toString().contains(defs.objectLiteralClassInfix); } private boolean isTransparent(ScopeKind kind) { return kind.isDefaultTransparent() || (kind.isOptional() && optionalKinds.contains(kind)); } protected abstract void reportForwardReference(DiagnosticPosition pos, boolean selfReference, Symbol s, boolean potential); } private List<ForwardReferenceChecker.ScopeKind> getForwardRefKinds() { String s = options.get("fwdRefOpt"); List<ForwardReferenceChecker.ScopeKind> kinds = List.nil(); if (s == null) { return kinds; } if (s.contains("objlit")) { kinds = kinds.append(ForwardReferenceChecker.ScopeKind.OBJ_LIT); kinds = kinds.append(ForwardReferenceChecker.ScopeKind.OBJ_LIT_FUNC); } if (s.contains("bind")) { kinds = kinds.append(ForwardReferenceChecker.ScopeKind.BOUND_CTX); } if (s.contains("interpolate")) { kinds = kinds.append(ForwardReferenceChecker.ScopeKind.INTERPOLATE_VALUE); } if (s.contains("keyframe")) { kinds = kinds.append(ForwardReferenceChecker.ScopeKind.KEYFRAME_LIT); } if (s.contains("lambda")) { kinds = kinds.append(ForwardReferenceChecker.ScopeKind.FUNCTION_VALUE); } if (s.contains("assign")) { kinds = kinds.append(ForwardReferenceChecker.ScopeKind.ASSIGN_CTX); } if (s.contains("onreplace") || s.contains("trigger")) { kinds = kinds.append(ForwardReferenceChecker.ScopeKind.ON_REPLACE); } if (s.contains("oninvalidate") || s.contains("trigger")) { kinds = kinds.append(ForwardReferenceChecker.ScopeKind.ON_INVALIDATE); } return kinds; } }