duke@1: /*
xdono@54: * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved.
duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1: *
duke@1: * This code is free software; you can redistribute it and/or modify it
duke@1: * under the terms of the GNU General Public License version 2 only, as
duke@1: * published by the Free Software Foundation. Sun designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
duke@1: * by Sun in the LICENSE file that accompanied this code.
duke@1: *
duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1: * version 2 for more details (a copy is included in the LICENSE file that
duke@1: * accompanied this code).
duke@1: *
duke@1: * You should have received a copy of the GNU General Public License version
duke@1: * 2 along with this work; if not, write to the Free Software Foundation,
duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1: *
duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or
duke@1: * have any questions.
duke@1: */
duke@1:
duke@1: package com.sun.tools.javac.comp;
duke@1:
duke@1: import java.util.*;
duke@1: import java.util.Set;
duke@1: import javax.lang.model.element.ElementKind;
duke@1: import javax.tools.JavaFileObject;
duke@1:
duke@1: import com.sun.tools.javac.code.*;
duke@1: import com.sun.tools.javac.jvm.*;
duke@1: import com.sun.tools.javac.tree.*;
duke@1: import com.sun.tools.javac.util.*;
duke@1: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
duke@1: import com.sun.tools.javac.util.List;
duke@1:
duke@1: import com.sun.tools.javac.jvm.Target;
duke@1: import com.sun.tools.javac.code.Symbol.*;
duke@1: import com.sun.tools.javac.tree.JCTree.*;
duke@1: import com.sun.tools.javac.code.Type.*;
duke@1:
duke@1: import com.sun.source.tree.IdentifierTree;
duke@1: import com.sun.source.tree.MemberSelectTree;
duke@1: import com.sun.source.tree.TreeVisitor;
duke@1: import com.sun.source.util.SimpleTreeVisitor;
duke@1:
duke@1: import static com.sun.tools.javac.code.Flags.*;
duke@1: import static com.sun.tools.javac.code.Kinds.*;
duke@1: import static com.sun.tools.javac.code.TypeTags.*;
duke@1:
duke@1: /** This is the main context-dependent analysis phase in GJC. It
duke@1: * encompasses name resolution, type checking and constant folding as
duke@1: * subtasks. Some subtasks involve auxiliary classes.
duke@1: * @see Check
duke@1: * @see Resolve
duke@1: * @see ConstFold
duke@1: * @see Infer
duke@1: *
duke@1: *
This is NOT part of any API supported by Sun Microsystems. If
duke@1: * you write code that depends on this, you do so at your own risk.
duke@1: * This code and its internal interfaces are subject to change or
duke@1: * deletion without notice.
duke@1: */
duke@1: public class Attr extends JCTree.Visitor {
duke@1: protected static final Context.Key attrKey =
duke@1: new Context.Key();
duke@1:
jjg@113: final Names names;
duke@1: final Log log;
duke@1: final Symtab syms;
duke@1: final Resolve rs;
duke@1: final Check chk;
duke@1: final MemberEnter memberEnter;
duke@1: final TreeMaker make;
duke@1: final ConstFold cfolder;
duke@1: final Enter enter;
duke@1: final Target target;
duke@1: final Types types;
mcimadamore@89: final JCDiagnostic.Factory diags;
duke@1: final Annotate annotate;
duke@1:
duke@1: public static Attr instance(Context context) {
duke@1: Attr instance = context.get(attrKey);
duke@1: if (instance == null)
duke@1: instance = new Attr(context);
duke@1: return instance;
duke@1: }
duke@1:
duke@1: protected Attr(Context context) {
duke@1: context.put(attrKey, this);
duke@1:
jjg@113: names = Names.instance(context);
duke@1: log = Log.instance(context);
duke@1: syms = Symtab.instance(context);
duke@1: rs = Resolve.instance(context);
duke@1: chk = Check.instance(context);
duke@1: memberEnter = MemberEnter.instance(context);
duke@1: make = TreeMaker.instance(context);
duke@1: enter = Enter.instance(context);
duke@1: cfolder = ConstFold.instance(context);
duke@1: target = Target.instance(context);
duke@1: types = Types.instance(context);
mcimadamore@89: diags = JCDiagnostic.Factory.instance(context);
duke@1: annotate = Annotate.instance(context);
duke@1:
duke@1: Options options = Options.instance(context);
duke@1:
duke@1: Source source = Source.instance(context);
duke@1: allowGenerics = source.allowGenerics();
duke@1: allowVarargs = source.allowVarargs();
duke@1: allowEnums = source.allowEnums();
duke@1: allowBoxing = source.allowBoxing();
duke@1: allowCovariantReturns = source.allowCovariantReturns();
duke@1: allowAnonOuterThis = source.allowAnonOuterThis();
duke@1: relax = (options.get("-retrofit") != null ||
duke@1: options.get("-relax") != null);
duke@1: useBeforeDeclarationWarning = options.get("useBeforeDeclarationWarning") != null;
duke@1: }
duke@1:
duke@1: /** Switch: relax some constraints for retrofit mode.
duke@1: */
duke@1: boolean relax;
duke@1:
duke@1: /** Switch: support generics?
duke@1: */
duke@1: boolean allowGenerics;
duke@1:
duke@1: /** Switch: allow variable-arity methods.
duke@1: */
duke@1: boolean allowVarargs;
duke@1:
duke@1: /** Switch: support enums?
duke@1: */
duke@1: boolean allowEnums;
duke@1:
duke@1: /** Switch: support boxing and unboxing?
duke@1: */
duke@1: boolean allowBoxing;
duke@1:
duke@1: /** Switch: support covariant result types?
duke@1: */
duke@1: boolean allowCovariantReturns;
duke@1:
duke@1: /** Switch: allow references to surrounding object from anonymous
duke@1: * objects during constructor call?
duke@1: */
duke@1: boolean allowAnonOuterThis;
duke@1:
duke@1: /**
duke@1: * Switch: warn about use of variable before declaration?
duke@1: * RFE: 6425594
duke@1: */
duke@1: boolean useBeforeDeclarationWarning;
duke@1:
duke@1: /** Check kind and type of given tree against protokind and prototype.
duke@1: * If check succeeds, store type in tree and return it.
duke@1: * If check fails, store errType in tree and return it.
duke@1: * No checks are performed if the prototype is a method type.
jjg@110: * It is not necessary in this case since we know that kind and type
duke@1: * are correct.
duke@1: *
duke@1: * @param tree The tree whose kind and type is checked
duke@1: * @param owntype The computed type of the tree
duke@1: * @param ownkind The computed kind of the tree
duke@1: * @param pkind The expected kind (or: protokind) of the tree
duke@1: * @param pt The expected type (or: prototype) of the tree
duke@1: */
duke@1: Type check(JCTree tree, Type owntype, int ownkind, int pkind, Type pt) {
duke@1: if (owntype.tag != ERROR && pt.tag != METHOD && pt.tag != FORALL) {
duke@1: if ((ownkind & ~pkind) == 0) {
duke@1: owntype = chk.checkType(tree.pos(), owntype, pt);
duke@1: } else {
duke@1: log.error(tree.pos(), "unexpected.type",
mcimadamore@80: kindNames(pkind),
mcimadamore@80: kindName(ownkind));
jjg@110: owntype = types.createErrorType(owntype);
duke@1: }
duke@1: }
duke@1: tree.type = owntype;
duke@1: return owntype;
duke@1: }
duke@1:
duke@1: /** Is given blank final variable assignable, i.e. in a scope where it
duke@1: * may be assigned to even though it is final?
duke@1: * @param v The blank final variable.
duke@1: * @param env The current environment.
duke@1: */
duke@1: boolean isAssignableAsBlankFinal(VarSymbol v, Env env) {
duke@1: Symbol owner = env.info.scope.owner;
duke@1: // owner refers to the innermost variable, method or
duke@1: // initializer block declaration at this point.
duke@1: return
duke@1: v.owner == owner
duke@1: ||
duke@1: ((owner.name == names.init || // i.e. we are in a constructor
duke@1: owner.kind == VAR || // i.e. we are in a variable initializer
duke@1: (owner.flags() & BLOCK) != 0) // i.e. we are in an initializer block
duke@1: &&
duke@1: v.owner == owner.owner
duke@1: &&
duke@1: ((v.flags() & STATIC) != 0) == Resolve.isStatic(env));
duke@1: }
duke@1:
duke@1: /** Check that variable can be assigned to.
duke@1: * @param pos The current source code position.
duke@1: * @param v The assigned varaible
duke@1: * @param base If the variable is referred to in a Select, the part
duke@1: * to the left of the `.', null otherwise.
duke@1: * @param env The current environment.
duke@1: */
duke@1: void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env env) {
duke@1: if ((v.flags() & FINAL) != 0 &&
duke@1: ((v.flags() & HASINIT) != 0
duke@1: ||
duke@1: !((base == null ||
duke@1: (base.getTag() == JCTree.IDENT && TreeInfo.name(base) == names._this)) &&
duke@1: isAssignableAsBlankFinal(v, env)))) {
duke@1: log.error(pos, "cant.assign.val.to.final.var", v);
duke@1: }
duke@1: }
duke@1:
duke@1: /** Does tree represent a static reference to an identifier?
duke@1: * It is assumed that tree is either a SELECT or an IDENT.
duke@1: * We have to weed out selects from non-type names here.
duke@1: * @param tree The candidate tree.
duke@1: */
duke@1: boolean isStaticReference(JCTree tree) {
duke@1: if (tree.getTag() == JCTree.SELECT) {
duke@1: Symbol lsym = TreeInfo.symbol(((JCFieldAccess) tree).selected);
duke@1: if (lsym == null || lsym.kind != TYP) {
duke@1: return false;
duke@1: }
duke@1: }
duke@1: return true;
duke@1: }
duke@1:
duke@1: /** Is this symbol a type?
duke@1: */
duke@1: static boolean isType(Symbol sym) {
duke@1: return sym != null && sym.kind == TYP;
duke@1: }
duke@1:
duke@1: /** The current `this' symbol.
duke@1: * @param env The current environment.
duke@1: */
duke@1: Symbol thisSym(DiagnosticPosition pos, Env env) {
duke@1: return rs.resolveSelf(pos, env, env.enclClass.sym, names._this);
duke@1: }
duke@1:
duke@1: /** Attribute a parsed identifier.
duke@1: * @param tree Parsed identifier name
duke@1: * @param topLevel The toplevel to use
duke@1: */
duke@1: public Symbol attribIdent(JCTree tree, JCCompilationUnit topLevel) {
duke@1: Env localEnv = enter.topLevelEnv(topLevel);
duke@1: localEnv.enclClass = make.ClassDef(make.Modifiers(0),
duke@1: syms.errSymbol.name,
duke@1: null, null, null, null);
duke@1: localEnv.enclClass.sym = syms.errSymbol;
duke@1: return tree.accept(identAttributer, localEnv);
duke@1: }
duke@1: // where
duke@1: private TreeVisitor> identAttributer = new IdentAttributer();
duke@1: private class IdentAttributer extends SimpleTreeVisitor> {
duke@1: @Override
duke@1: public Symbol visitMemberSelect(MemberSelectTree node, Env env) {
duke@1: Symbol site = visit(node.getExpression(), env);
duke@1: if (site.kind == ERR)
duke@1: return site;
duke@1: Name name = (Name)node.getIdentifier();
duke@1: if (site.kind == PCK) {
duke@1: env.toplevel.packge = (PackageSymbol)site;
duke@1: return rs.findIdentInPackage(env, (TypeSymbol)site, name, TYP | PCK);
duke@1: } else {
duke@1: env.enclClass.sym = (ClassSymbol)site;
duke@1: return rs.findMemberType(env, site.asType(), name, (TypeSymbol)site);
duke@1: }
duke@1: }
duke@1:
duke@1: @Override
duke@1: public Symbol visitIdentifier(IdentifierTree node, Env env) {
duke@1: return rs.findIdent(env, (Name)node.getName(), TYP | PCK);
duke@1: }
duke@1: }
duke@1:
duke@1: public Type coerce(Type etype, Type ttype) {
duke@1: return cfolder.coerce(etype, ttype);
duke@1: }
duke@1:
duke@1: public Type attribType(JCTree node, TypeSymbol sym) {
duke@1: Env env = enter.typeEnvs.get(sym);
duke@1: Env localEnv = env.dup(node, env.info.dup());
duke@1: return attribTree(node, localEnv, Kinds.TYP, Type.noType);
duke@1: }
duke@1:
duke@1: public Env attribExprToTree(JCTree expr, Env env, JCTree tree) {
duke@1: breakTree = tree;
duke@1: JavaFileObject prev = log.useSource(null);
duke@1: try {
duke@1: attribExpr(expr, env);
duke@1: } catch (BreakAttr b) {
duke@1: return b.env;
duke@1: } finally {
duke@1: breakTree = null;
duke@1: log.useSource(prev);
duke@1: }
duke@1: return env;
duke@1: }
duke@1:
duke@1: public Env attribStatToTree(JCTree stmt, Env env, JCTree tree) {
duke@1: breakTree = tree;
duke@1: JavaFileObject prev = log.useSource(null);
duke@1: try {
duke@1: attribStat(stmt, env);
duke@1: } catch (BreakAttr b) {
duke@1: return b.env;
duke@1: } finally {
duke@1: breakTree = null;
duke@1: log.useSource(prev);
duke@1: }
duke@1: return env;
duke@1: }
duke@1:
duke@1: private JCTree breakTree = null;
duke@1:
duke@1: private static class BreakAttr extends RuntimeException {
duke@1: static final long serialVersionUID = -6924771130405446405L;
duke@1: private Env env;
duke@1: private BreakAttr(Env env) {
duke@1: this.env = env;
duke@1: }
duke@1: }
duke@1:
duke@1:
duke@1: /* ************************************************************************
duke@1: * Visitor methods
duke@1: *************************************************************************/
duke@1:
duke@1: /** Visitor argument: the current environment.
duke@1: */
duke@1: Env env;
duke@1:
duke@1: /** Visitor argument: the currently expected proto-kind.
duke@1: */
duke@1: int pkind;
duke@1:
duke@1: /** Visitor argument: the currently expected proto-type.
duke@1: */
duke@1: Type pt;
duke@1:
duke@1: /** Visitor result: the computed type.
duke@1: */
duke@1: Type result;
duke@1:
duke@1: /** Visitor method: attribute a tree, catching any completion failure
duke@1: * exceptions. Return the tree's type.
duke@1: *
duke@1: * @param tree The tree to be visited.
duke@1: * @param env The environment visitor argument.
duke@1: * @param pkind The protokind visitor argument.
duke@1: * @param pt The prototype visitor argument.
duke@1: */
duke@1: Type attribTree(JCTree tree, Env env, int pkind, Type pt) {
duke@1: Env prevEnv = this.env;
duke@1: int prevPkind = this.pkind;
duke@1: Type prevPt = this.pt;
duke@1: try {
duke@1: this.env = env;
duke@1: this.pkind = pkind;
duke@1: this.pt = pt;
duke@1: tree.accept(this);
duke@1: if (tree == breakTree)
duke@1: throw new BreakAttr(env);
duke@1: return result;
duke@1: } catch (CompletionFailure ex) {
duke@1: tree.type = syms.errType;
duke@1: return chk.completionError(tree.pos(), ex);
duke@1: } finally {
duke@1: this.env = prevEnv;
duke@1: this.pkind = prevPkind;
duke@1: this.pt = prevPt;
duke@1: }
duke@1: }
duke@1:
duke@1: /** Derived visitor method: attribute an expression tree.
duke@1: */
duke@1: public Type attribExpr(JCTree tree, Env env, Type pt) {
duke@1: return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType);
duke@1: }
duke@1:
duke@1: /** Derived visitor method: attribute an expression tree with
duke@1: * no constraints on the computed type.
duke@1: */
duke@1: Type attribExpr(JCTree tree, Env env) {
duke@1: return attribTree(tree, env, VAL, Type.noType);
duke@1: }
duke@1:
duke@1: /** Derived visitor method: attribute a type tree.
duke@1: */
duke@1: Type attribType(JCTree tree, Env env) {
duke@1: Type result = attribTree(tree, env, TYP, Type.noType);
duke@1: return result;
duke@1: }
duke@1:
duke@1: /** Derived visitor method: attribute a statement or definition tree.
duke@1: */
duke@1: public Type attribStat(JCTree tree, Env env) {
duke@1: return attribTree(tree, env, NIL, Type.noType);
duke@1: }
duke@1:
duke@1: /** Attribute a list of expressions, returning a list of types.
duke@1: */
duke@1: List attribExprs(List trees, Env env, Type pt) {
duke@1: ListBuffer ts = new ListBuffer();
duke@1: for (List l = trees; l.nonEmpty(); l = l.tail)
duke@1: ts.append(attribExpr(l.head, env, pt));
duke@1: return ts.toList();
duke@1: }
duke@1:
duke@1: /** Attribute a list of statements, returning nothing.
duke@1: */
duke@1: void attribStats(List trees, Env env) {
duke@1: for (List l = trees; l.nonEmpty(); l = l.tail)
duke@1: attribStat(l.head, env);
duke@1: }
duke@1:
duke@1: /** Attribute the arguments in a method call, returning a list of types.
duke@1: */
duke@1: List attribArgs(List trees, Env env) {
duke@1: ListBuffer argtypes = new ListBuffer();
duke@1: for (List l = trees; l.nonEmpty(); l = l.tail)
duke@1: argtypes.append(chk.checkNonVoid(
duke@1: l.head.pos(), types.upperBound(attribTree(l.head, env, VAL, Infer.anyPoly))));
duke@1: return argtypes.toList();
duke@1: }
duke@1:
duke@1: /** Attribute a type argument list, returning a list of types.
duke@1: */
duke@1: List attribTypes(List trees, Env env) {
duke@1: ListBuffer argtypes = new ListBuffer();
duke@1: for (List l = trees; l.nonEmpty(); l = l.tail)
duke@1: argtypes.append(chk.checkRefType(l.head.pos(), attribType(l.head, env)));
duke@1: return argtypes.toList();
duke@1: }
duke@1:
duke@1:
duke@1: /**
duke@1: * Attribute type variables (of generic classes or methods).
duke@1: * Compound types are attributed later in attribBounds.
duke@1: * @param typarams the type variables to enter
duke@1: * @param env the current environment
duke@1: */
duke@1: void attribTypeVariables(List typarams, Env env) {
duke@1: for (JCTypeParameter tvar : typarams) {
duke@1: TypeVar a = (TypeVar)tvar.type;
mcimadamore@42: a.tsym.flags_field |= UNATTRIBUTED;
mcimadamore@42: a.bound = Type.noType;
duke@1: if (!tvar.bounds.isEmpty()) {
duke@1: List bounds = List.of(attribType(tvar.bounds.head, env));
duke@1: for (JCExpression bound : tvar.bounds.tail)
duke@1: bounds = bounds.prepend(attribType(bound, env));
duke@1: types.setBounds(a, bounds.reverse());
duke@1: } else {
duke@1: // if no bounds are given, assume a single bound of
duke@1: // java.lang.Object.
duke@1: types.setBounds(a, List.of(syms.objectType));
duke@1: }
mcimadamore@42: a.tsym.flags_field &= ~UNATTRIBUTED;
duke@1: }
duke@1: for (JCTypeParameter tvar : typarams)
duke@1: chk.checkNonCyclic(tvar.pos(), (TypeVar)tvar.type);
duke@1: attribStats(typarams, env);
mcimadamore@42: }
mcimadamore@42:
mcimadamore@42: void attribBounds(List typarams) {
duke@1: for (JCTypeParameter typaram : typarams) {
duke@1: Type bound = typaram.type.getUpperBound();
duke@1: if (bound != null && bound.tsym instanceof ClassSymbol) {
duke@1: ClassSymbol c = (ClassSymbol)bound.tsym;
duke@1: if ((c.flags_field & COMPOUND) != 0) {
duke@1: assert (c.flags_field & UNATTRIBUTED) != 0 : c;
duke@1: attribClass(typaram.pos(), c);
duke@1: }
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Attribute the type references in a list of annotations.
duke@1: */
duke@1: void attribAnnotationTypes(List annotations,
duke@1: Env env) {
duke@1: for (List al = annotations; al.nonEmpty(); al = al.tail) {
duke@1: JCAnnotation a = al.head;
duke@1: attribType(a.annotationType, env);
duke@1: }
duke@1: }
duke@1:
duke@1: /** Attribute type reference in an `extends' or `implements' clause.
duke@1: *
duke@1: * @param tree The tree making up the type reference.
duke@1: * @param env The environment current at the reference.
duke@1: * @param classExpected true if only a class is expected here.
duke@1: * @param interfaceExpected true if only an interface is expected here.
duke@1: */
duke@1: Type attribBase(JCTree tree,
duke@1: Env env,
duke@1: boolean classExpected,
duke@1: boolean interfaceExpected,
duke@1: boolean checkExtensible) {
duke@1: Type t = attribType(tree, env);
duke@1: return checkBase(t, tree, env, classExpected, interfaceExpected, checkExtensible);
duke@1: }
duke@1: Type checkBase(Type t,
duke@1: JCTree tree,
duke@1: Env env,
duke@1: boolean classExpected,
duke@1: boolean interfaceExpected,
duke@1: boolean checkExtensible) {
duke@1: if (t.tag == TYPEVAR && !classExpected && !interfaceExpected) {
duke@1: // check that type variable is already visible
duke@1: if (t.getUpperBound() == null) {
duke@1: log.error(tree.pos(), "illegal.forward.ref");
jjg@110: return types.createErrorType(t);
duke@1: }
duke@1: } else {
duke@1: t = chk.checkClassType(tree.pos(), t, checkExtensible|!allowGenerics);
duke@1: }
duke@1: if (interfaceExpected && (t.tsym.flags() & INTERFACE) == 0) {
duke@1: log.error(tree.pos(), "intf.expected.here");
duke@1: // return errType is necessary since otherwise there might
duke@1: // be undetected cycles which cause attribution to loop
jjg@110: return types.createErrorType(t);
duke@1: } else if (checkExtensible &&
duke@1: classExpected &&
duke@1: (t.tsym.flags() & INTERFACE) != 0) {
duke@1: log.error(tree.pos(), "no.intf.expected.here");
jjg@110: return types.createErrorType(t);
duke@1: }
duke@1: if (checkExtensible &&
duke@1: ((t.tsym.flags() & FINAL) != 0)) {
duke@1: log.error(tree.pos(),
duke@1: "cant.inherit.from.final", t.tsym);
duke@1: }
duke@1: chk.checkNonCyclic(tree.pos(), t);
duke@1: return t;
duke@1: }
duke@1:
duke@1: public void visitClassDef(JCClassDecl tree) {
duke@1: // Local classes have not been entered yet, so we need to do it now:
duke@1: if ((env.info.scope.owner.kind & (VAR | MTH)) != 0)
duke@1: enter.classEnter(tree, env);
duke@1:
duke@1: ClassSymbol c = tree.sym;
duke@1: if (c == null) {
duke@1: // exit in case something drastic went wrong during enter.
duke@1: result = null;
duke@1: } else {
duke@1: // make sure class has been completed:
duke@1: c.complete();
duke@1:
duke@1: // If this class appears as an anonymous class
duke@1: // in a superclass constructor call where
duke@1: // no explicit outer instance is given,
duke@1: // disable implicit outer instance from being passed.
duke@1: // (This would be an illegal access to "this before super").
duke@1: if (env.info.isSelfCall &&
duke@1: env.tree.getTag() == JCTree.NEWCLASS &&
duke@1: ((JCNewClass) env.tree).encl == null)
duke@1: {
duke@1: c.flags_field |= NOOUTERTHIS;
duke@1: }
duke@1: attribClass(tree.pos(), c);
duke@1: result = tree.type = c.type;
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitMethodDef(JCMethodDecl tree) {
duke@1: MethodSymbol m = tree.sym;
duke@1:
duke@1: Lint lint = env.info.lint.augment(m.attributes_field, m.flags());
duke@1: Lint prevLint = chk.setLint(lint);
duke@1: try {
duke@1: chk.checkDeprecatedAnnotation(tree.pos(), m);
duke@1:
mcimadamore@42: attribBounds(tree.typarams);
duke@1:
duke@1: // If we override any other methods, check that we do so properly.
duke@1: // JLS ???
duke@1: chk.checkOverride(tree, m);
duke@1:
duke@1: // Create a new environment with local scope
duke@1: // for attributing the method.
duke@1: Env localEnv = memberEnter.methodEnv(tree, env);
duke@1:
duke@1: localEnv.info.lint = lint;
duke@1:
duke@1: // Enter all type parameters into the local method scope.
duke@1: for (List l = tree.typarams; l.nonEmpty(); l = l.tail)
duke@1: localEnv.info.scope.enterIfAbsent(l.head.type.tsym);
duke@1:
duke@1: ClassSymbol owner = env.enclClass.sym;
duke@1: if ((owner.flags() & ANNOTATION) != 0 &&
duke@1: tree.params.nonEmpty())
duke@1: log.error(tree.params.head.pos(),
duke@1: "intf.annotation.members.cant.have.params");
duke@1:
duke@1: // Attribute all value parameters.
duke@1: for (List l = tree.params; l.nonEmpty(); l = l.tail) {
duke@1: attribStat(l.head, localEnv);
duke@1: }
duke@1:
duke@1: // Check that type parameters are well-formed.
mcimadamore@122: chk.validate(tree.typarams, localEnv);
duke@1: if ((owner.flags() & ANNOTATION) != 0 &&
duke@1: tree.typarams.nonEmpty())
duke@1: log.error(tree.typarams.head.pos(),
duke@1: "intf.annotation.members.cant.have.type.params");
duke@1:
duke@1: // Check that result type is well-formed.
mcimadamore@122: chk.validate(tree.restype, localEnv);
duke@1: if ((owner.flags() & ANNOTATION) != 0)
duke@1: chk.validateAnnotationType(tree.restype);
duke@1:
duke@1: if ((owner.flags() & ANNOTATION) != 0)
duke@1: chk.validateAnnotationMethod(tree.pos(), m);
duke@1:
duke@1: // Check that all exceptions mentioned in the throws clause extend
duke@1: // java.lang.Throwable.
duke@1: if ((owner.flags() & ANNOTATION) != 0 && tree.thrown.nonEmpty())
duke@1: log.error(tree.thrown.head.pos(),
duke@1: "throws.not.allowed.in.intf.annotation");
duke@1: for (List l = tree.thrown; l.nonEmpty(); l = l.tail)
duke@1: chk.checkType(l.head.pos(), l.head.type, syms.throwableType);
duke@1:
duke@1: if (tree.body == null) {
duke@1: // Empty bodies are only allowed for
duke@1: // abstract, native, or interface methods, or for methods
duke@1: // in a retrofit signature class.
duke@1: if ((owner.flags() & INTERFACE) == 0 &&
duke@1: (tree.mods.flags & (ABSTRACT | NATIVE)) == 0 &&
duke@1: !relax)
duke@1: log.error(tree.pos(), "missing.meth.body.or.decl.abstract");
duke@1: if (tree.defaultValue != null) {
duke@1: if ((owner.flags() & ANNOTATION) == 0)
duke@1: log.error(tree.pos(),
duke@1: "default.allowed.in.intf.annotation.member");
duke@1: }
duke@1: } else if ((owner.flags() & INTERFACE) != 0) {
duke@1: log.error(tree.body.pos(), "intf.meth.cant.have.body");
duke@1: } else if ((tree.mods.flags & ABSTRACT) != 0) {
duke@1: log.error(tree.pos(), "abstract.meth.cant.have.body");
duke@1: } else if ((tree.mods.flags & NATIVE) != 0) {
duke@1: log.error(tree.pos(), "native.meth.cant.have.body");
duke@1: } else {
duke@1: // Add an implicit super() call unless an explicit call to
duke@1: // super(...) or this(...) is given
duke@1: // or we are compiling class java.lang.Object.
duke@1: if (tree.name == names.init && owner.type != syms.objectType) {
duke@1: JCBlock body = tree.body;
duke@1: if (body.stats.isEmpty() ||
duke@1: !TreeInfo.isSelfCall(body.stats.head)) {
duke@1: body.stats = body.stats.
duke@1: prepend(memberEnter.SuperCall(make.at(body.pos),
duke@1: List.nil(),
duke@1: List.nil(),
duke@1: false));
duke@1: } else if ((env.enclClass.sym.flags() & ENUM) != 0 &&
duke@1: (tree.mods.flags & GENERATEDCONSTR) == 0 &&
duke@1: TreeInfo.isSuperCall(body.stats.head)) {
duke@1: // enum constructors are not allowed to call super
duke@1: // directly, so make sure there aren't any super calls
duke@1: // in enum constructors, except in the compiler
duke@1: // generated one.
duke@1: log.error(tree.body.stats.head.pos(),
duke@1: "call.to.super.not.allowed.in.enum.ctor",
duke@1: env.enclClass.sym);
duke@1: }
duke@1: }
duke@1:
duke@1: // Attribute method body.
duke@1: attribStat(tree.body, localEnv);
duke@1: }
duke@1: localEnv.info.scope.leave();
duke@1: result = tree.type = m.type;
duke@1: chk.validateAnnotations(tree.mods.annotations, m);
duke@1:
duke@1: }
duke@1: finally {
duke@1: chk.setLint(prevLint);
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitVarDef(JCVariableDecl tree) {
duke@1: // Local variables have not been entered yet, so we need to do it now:
duke@1: if (env.info.scope.owner.kind == MTH) {
duke@1: if (tree.sym != null) {
duke@1: // parameters have already been entered
duke@1: env.info.scope.enter(tree.sym);
duke@1: } else {
duke@1: memberEnter.memberEnter(tree, env);
duke@1: annotate.flush();
duke@1: }
duke@1: }
duke@1:
duke@1: // Check that the variable's declared type is well-formed.
mcimadamore@122: chk.validate(tree.vartype, env);
duke@1:
duke@1: VarSymbol v = tree.sym;
duke@1: Lint lint = env.info.lint.augment(v.attributes_field, v.flags());
duke@1: Lint prevLint = chk.setLint(lint);
duke@1:
duke@1: try {
duke@1: chk.checkDeprecatedAnnotation(tree.pos(), v);
duke@1:
duke@1: if (tree.init != null) {
duke@1: if ((v.flags_field & FINAL) != 0 && tree.init.getTag() != JCTree.NEWCLASS) {
duke@1: // In this case, `v' is final. Ensure that it's initializer is
duke@1: // evaluated.
duke@1: v.getConstValue(); // ensure initializer is evaluated
duke@1: } else {
duke@1: // Attribute initializer in a new environment
duke@1: // with the declared variable as owner.
duke@1: // Check that initializer conforms to variable's declared type.
duke@1: Env initEnv = memberEnter.initEnv(tree, env);
duke@1: initEnv.info.lint = lint;
duke@1: // In order to catch self-references, we set the variable's
duke@1: // declaration position to maximal possible value, effectively
duke@1: // marking the variable as undefined.
mcimadamore@94: initEnv.info.enclVar = v;
duke@1: attribExpr(tree.init, initEnv, v.type);
duke@1: }
duke@1: }
duke@1: result = tree.type = v.type;
duke@1: chk.validateAnnotations(tree.mods.annotations, v);
duke@1: }
duke@1: finally {
duke@1: chk.setLint(prevLint);
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitSkip(JCSkip tree) {
duke@1: result = null;
duke@1: }
duke@1:
duke@1: public void visitBlock(JCBlock tree) {
duke@1: if (env.info.scope.owner.kind == TYP) {
duke@1: // Block is a static or instance initializer;
duke@1: // let the owner of the environment be a freshly
duke@1: // created BLOCK-method.
duke@1: Env localEnv =
duke@1: env.dup(tree, env.info.dup(env.info.scope.dupUnshared()));
duke@1: localEnv.info.scope.owner =
duke@1: new MethodSymbol(tree.flags | BLOCK, names.empty, null,
duke@1: env.info.scope.owner);
duke@1: if ((tree.flags & STATIC) != 0) localEnv.info.staticLevel++;
duke@1: attribStats(tree.stats, localEnv);
duke@1: } else {
duke@1: // Create a new local environment with a local scope.
duke@1: Env localEnv =
duke@1: env.dup(tree, env.info.dup(env.info.scope.dup()));
duke@1: attribStats(tree.stats, localEnv);
duke@1: localEnv.info.scope.leave();
duke@1: }
duke@1: result = null;
duke@1: }
duke@1:
duke@1: public void visitDoLoop(JCDoWhileLoop tree) {
duke@1: attribStat(tree.body, env.dup(tree));
duke@1: attribExpr(tree.cond, env, syms.booleanType);
duke@1: result = null;
duke@1: }
duke@1:
duke@1: public void visitWhileLoop(JCWhileLoop tree) {
duke@1: attribExpr(tree.cond, env, syms.booleanType);
duke@1: attribStat(tree.body, env.dup(tree));
duke@1: result = null;
duke@1: }
duke@1:
duke@1: public void visitForLoop(JCForLoop tree) {
duke@1: Env loopEnv =
duke@1: env.dup(env.tree, env.info.dup(env.info.scope.dup()));
duke@1: attribStats(tree.init, loopEnv);
duke@1: if (tree.cond != null) attribExpr(tree.cond, loopEnv, syms.booleanType);
duke@1: loopEnv.tree = tree; // before, we were not in loop!
duke@1: attribStats(tree.step, loopEnv);
duke@1: attribStat(tree.body, loopEnv);
duke@1: loopEnv.info.scope.leave();
duke@1: result = null;
duke@1: }
duke@1:
duke@1: public void visitForeachLoop(JCEnhancedForLoop tree) {
duke@1: Env loopEnv =
duke@1: env.dup(env.tree, env.info.dup(env.info.scope.dup()));
duke@1: attribStat(tree.var, loopEnv);
duke@1: Type exprType = types.upperBound(attribExpr(tree.expr, loopEnv));
duke@1: chk.checkNonVoid(tree.pos(), exprType);
duke@1: Type elemtype = types.elemtype(exprType); // perhaps expr is an array?
duke@1: if (elemtype == null) {
duke@1: // or perhaps expr implements Iterable?
duke@1: Type base = types.asSuper(exprType, syms.iterableType.tsym);
duke@1: if (base == null) {
duke@1: log.error(tree.expr.pos(), "foreach.not.applicable.to.type");
jjg@110: elemtype = types.createErrorType(exprType);
duke@1: } else {
duke@1: List iterableParams = base.allparams();
duke@1: elemtype = iterableParams.isEmpty()
duke@1: ? syms.objectType
duke@1: : types.upperBound(iterableParams.head);
duke@1: }
duke@1: }
duke@1: chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type);
duke@1: loopEnv.tree = tree; // before, we were not in loop!
duke@1: attribStat(tree.body, loopEnv);
duke@1: loopEnv.info.scope.leave();
duke@1: result = null;
duke@1: }
duke@1:
duke@1: public void visitLabelled(JCLabeledStatement tree) {
duke@1: // Check that label is not used in an enclosing statement
duke@1: Env env1 = env;
duke@1: while (env1 != null && env1.tree.getTag() != JCTree.CLASSDEF) {
duke@1: if (env1.tree.getTag() == JCTree.LABELLED &&
duke@1: ((JCLabeledStatement) env1.tree).label == tree.label) {
duke@1: log.error(tree.pos(), "label.already.in.use",
duke@1: tree.label);
duke@1: break;
duke@1: }
duke@1: env1 = env1.next;
duke@1: }
duke@1:
duke@1: attribStat(tree.body, env.dup(tree));
duke@1: result = null;
duke@1: }
duke@1:
duke@1: public void visitSwitch(JCSwitch tree) {
duke@1: Type seltype = attribExpr(tree.selector, env);
duke@1:
duke@1: Env switchEnv =
duke@1: env.dup(tree, env.info.dup(env.info.scope.dup()));
duke@1:
duke@1: boolean enumSwitch =
duke@1: allowEnums &&
duke@1: (seltype.tsym.flags() & Flags.ENUM) != 0;
duke@1: if (!enumSwitch)
duke@1: seltype = chk.checkType(tree.selector.pos(), seltype, syms.intType);
duke@1:
duke@1: // Attribute all cases and
duke@1: // check that there are no duplicate case labels or default clauses.
duke@1: Set