/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.comp;
import java.util.*;
import java.util.Set;
import javax.lang.model.element.ElementKind;
import javax.tools.JavaFileObject;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.jvm.*;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.code.Type.*;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.TreeVisitor;
import com.sun.source.util.SimpleTreeVisitor;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.*;
import static com.sun.tools.javac.code.TypeTags.*;
/** This is the main context-dependent analysis phase in GJC. It
* encompasses name resolution, type checking and constant folding as
* subtasks. Some subtasks involve auxiliary classes.
* @see Check
* @see Resolve
* @see ConstFold
* @see Infer
*
*
This is NOT part of any supported API.
* 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.
*/
public class Attr extends JCTree.Visitor {
protected static final Context.Key attrKey =
new Context.Key();
final Names names;
final Log log;
final Symtab syms;
final Resolve rs;
final Infer infer;
final Check chk;
final MemberEnter memberEnter;
final TreeMaker make;
final ConstFold cfolder;
final Enter enter;
final Target target;
final Types types;
final JCDiagnostic.Factory diags;
final Annotate annotate;
final DeferredLintHandler deferredLintHandler;
public static Attr instance(Context context) {
Attr instance = context.get(attrKey);
if (instance == null)
instance = new Attr(context);
return instance;
}
protected Attr(Context context) {
context.put(attrKey, this);
names = Names.instance(context);
log = Log.instance(context);
syms = Symtab.instance(context);
rs = Resolve.instance(context);
chk = Check.instance(context);
memberEnter = MemberEnter.instance(context);
make = TreeMaker.instance(context);
enter = Enter.instance(context);
infer = Infer.instance(context);
cfolder = ConstFold.instance(context);
target = Target.instance(context);
types = Types.instance(context);
diags = JCDiagnostic.Factory.instance(context);
annotate = Annotate.instance(context);
deferredLintHandler = DeferredLintHandler.instance(context);
Options options = Options.instance(context);
Source source = Source.instance(context);
allowGenerics = source.allowGenerics();
allowVarargs = source.allowVarargs();
allowEnums = source.allowEnums();
allowBoxing = source.allowBoxing();
allowCovariantReturns = source.allowCovariantReturns();
allowAnonOuterThis = source.allowAnonOuterThis();
allowStringsInSwitch = source.allowStringsInSwitch();
sourceName = source.name;
relax = (options.isSet("-retrofit") ||
options.isSet("-relax"));
findDiamonds = options.get("findDiamond") != null &&
source.allowDiamond();
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
}
/** Switch: relax some constraints for retrofit mode.
*/
boolean relax;
/** Switch: support generics?
*/
boolean allowGenerics;
/** Switch: allow variable-arity methods.
*/
boolean allowVarargs;
/** Switch: support enums?
*/
boolean allowEnums;
/** Switch: support boxing and unboxing?
*/
boolean allowBoxing;
/** Switch: support covariant result types?
*/
boolean allowCovariantReturns;
/** Switch: allow references to surrounding object from anonymous
* objects during constructor call?
*/
boolean allowAnonOuterThis;
/** Switch: generates a warning if diamond can be safely applied
* to a given new expression
*/
boolean findDiamonds;
/**
* Internally enables/disables diamond finder feature
*/
static final boolean allowDiamondFinder = true;
/**
* Switch: warn about use of variable before declaration?
* RFE: 6425594
*/
boolean useBeforeDeclarationWarning;
/**
* Switch: allow strings in switch?
*/
boolean allowStringsInSwitch;
/**
* Switch: name of source level; used for error reporting.
*/
String sourceName;
/** Check kind and type of given tree against protokind and prototype.
* If check succeeds, store type in tree and return it.
* If check fails, store errType in tree and return it.
* No checks are performed if the prototype is a method type.
* It is not necessary in this case since we know that kind and type
* are correct.
*
* @param tree The tree whose kind and type is checked
* @param owntype The computed type of the tree
* @param ownkind The computed kind of the tree
* @param pkind The expected kind (or: protokind) of the tree
* @param pt The expected type (or: prototype) of the tree
*/
Type check(JCTree tree, Type owntype, int ownkind, int pkind, Type pt) {
if (owntype.tag != ERROR && pt.tag != METHOD && pt.tag != FORALL) {
if ((ownkind & ~pkind) == 0) {
owntype = chk.checkType(tree.pos(), owntype, pt, errKey);
} else {
log.error(tree.pos(), "unexpected.type",
kindNames(pkind),
kindName(ownkind));
owntype = types.createErrorType(owntype);
}
}
tree.type = owntype;
return owntype;
}
/** 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(VarSymbol v, Env 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) == Resolve.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, VarSymbol v, JCTree base, Env env) {
if ((v.flags() & FINAL) != 0 &&
((v.flags() & HASINIT) != 0
||
!((base == null ||
(base.getTag() == JCTree.IDENT && TreeInfo.name(base) == names._this)) &&
isAssignableAsBlankFinal(v, env)))) {
if (v.isResourceVariable()) { //TWR resource
log.error(pos, "try.resource.may.not.be.assigned", v);
} else {
log.error(pos, "cant.assign.val.to.final.var", v);
}
} else if ((v.flags() & EFFECTIVELY_FINAL) != 0) {
v.flags_field &= ~EFFECTIVELY_FINAL;
}
}
/** Does tree represent a static reference to an identifier?
* It is assumed that tree is either a SELECT or an IDENT.
* We have to weed out selects from non-type names here.
* @param tree The candidate tree.
*/
boolean isStaticReference(JCTree tree) {
if (tree.getTag() == JCTree.SELECT) {
Symbol lsym = TreeInfo.symbol(((JCFieldAccess) tree).selected);
if (lsym == null || lsym.kind != TYP) {
return false;
}
}
return true;
}
/** Is this symbol a type?
*/
static boolean isType(Symbol sym) {
return sym != null && sym.kind == TYP;
}
/** The current `this' symbol.
* @param env The current environment.
*/
Symbol thisSym(DiagnosticPosition pos, Env env) {
return rs.resolveSelf(pos, env, env.enclClass.sym, names._this);
}
/** Attribute a parsed identifier.
* @param tree Parsed identifier name
* @param topLevel The toplevel to use
*/
public Symbol attribIdent(JCTree tree, JCCompilationUnit topLevel) {
Env localEnv = enter.topLevelEnv(topLevel);
localEnv.enclClass = make.ClassDef(make.Modifiers(0),
syms.errSymbol.name,
null, null, null, null);
localEnv.enclClass.sym = syms.errSymbol;
return tree.accept(identAttributer, localEnv);
}
// where
private TreeVisitor> identAttributer = new IdentAttributer();
private class IdentAttributer extends SimpleTreeVisitor> {
@Override
public Symbol visitMemberSelect(MemberSelectTree node, Env env) {
Symbol site = visit(node.getExpression(), env);
if (site.kind == ERR)
return site;
Name name = (Name)node.getIdentifier();
if (site.kind == PCK) {
env.toplevel.packge = (PackageSymbol)site;
return rs.findIdentInPackage(env, (TypeSymbol)site, name, TYP | PCK);
} else {
env.enclClass.sym = (ClassSymbol)site;
return rs.findMemberType(env, site.asType(), name, (TypeSymbol)site);
}
}
@Override
public Symbol visitIdentifier(IdentifierTree node, Env env) {
return rs.findIdent(env, (Name)node.getName(), TYP | PCK);
}
}
public Type coerce(Type etype, Type ttype) {
return cfolder.coerce(etype, ttype);
}
public Type attribType(JCTree node, TypeSymbol sym) {
Env env = enter.typeEnvs.get(sym);
Env localEnv = env.dup(node, env.info.dup());
return attribTree(node, localEnv, Kinds.TYP, Type.noType);
}
public Env attribExprToTree(JCTree expr, Env env, JCTree tree) {
breakTree = tree;
JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
try {
attribExpr(expr, env);
} catch (BreakAttr b) {
return b.env;
} catch (AssertionError ae) {
if (ae.getCause() instanceof BreakAttr) {
return ((BreakAttr)(ae.getCause())).env;
} else {
throw ae;
}
} finally {
breakTree = null;
log.useSource(prev);
}
return env;
}
public Env attribStatToTree(JCTree stmt, Env env, JCTree tree) {
breakTree = tree;
JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
try {
attribStat(stmt, env);
} catch (BreakAttr b) {
return b.env;
} catch (AssertionError ae) {
if (ae.getCause() instanceof BreakAttr) {
return ((BreakAttr)(ae.getCause())).env;
} else {
throw ae;
}
} finally {
breakTree = null;
log.useSource(prev);
}
return env;
}
private JCTree breakTree = null;
private static class BreakAttr extends RuntimeException {
static final long serialVersionUID = -6924771130405446405L;
private Env env;
private BreakAttr(Env env) {
this.env = env;
}
}
/* ************************************************************************
* Visitor methods
*************************************************************************/
/** Visitor argument: the current environment.
*/
Env env;
/** Visitor argument: the currently expected proto-kind.
*/
int pkind;
/** Visitor argument: the currently expected proto-type.
*/
Type pt;
/** Visitor argument: the error key to be generated when a type error occurs
*/
String errKey;
/** Visitor result: the computed type.
*/
Type result;
/** Visitor method: attribute a tree, catching any completion failure
* exceptions. Return the tree's type.
*
* @param tree The tree to be visited.
* @param env The environment visitor argument.
* @param pkind The protokind visitor argument.
* @param pt The prototype visitor argument.
*/
Type attribTree(JCTree tree, Env env, int pkind, Type pt) {
return attribTree(tree, env, pkind, pt, "incompatible.types");
}
Type attribTree(JCTree tree, Env env, int pkind, Type pt, String errKey) {
Env prevEnv = this.env;
int prevPkind = this.pkind;
Type prevPt = this.pt;
String prevErrKey = this.errKey;
try {
this.env = env;
this.pkind = pkind;
this.pt = pt;
this.errKey = errKey;
tree.accept(this);
if (tree == breakTree)
throw new BreakAttr(env);
return result;
} catch (CompletionFailure ex) {
tree.type = syms.errType;
return chk.completionError(tree.pos(), ex);
} finally {
this.env = prevEnv;
this.pkind = prevPkind;
this.pt = prevPt;
this.errKey = prevErrKey;
}
}
/** Derived visitor method: attribute an expression tree.
*/
public Type attribExpr(JCTree tree, Env env, Type pt) {
return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType);
}
public Type attribExpr(JCTree tree, Env env, Type pt, String key) {
return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType, key);
}
/** Derived visitor method: attribute an expression tree with
* no constraints on the computed type.
*/
Type attribExpr(JCTree tree, Env env) {
return attribTree(tree, env, VAL, Type.noType);
}
/** Derived visitor method: attribute a type tree.
*/
Type attribType(JCTree tree, Env env) {
Type result = attribType(tree, env, Type.noType);
return result;
}
/** Derived visitor method: attribute a type tree.
*/
Type attribType(JCTree tree, Env env, Type pt) {
Type result = attribTree(tree, env, TYP, pt);
return result;
}
/** Derived visitor method: attribute a statement or definition tree.
*/
public Type attribStat(JCTree tree, Env env) {
return attribTree(tree, env, NIL, Type.noType);
}
/** Attribute a list of expressions, returning a list of types.
*/
List attribExprs(List trees, Env env, Type pt) {
ListBuffer ts = new ListBuffer();
for (List l = trees; l.nonEmpty(); l = l.tail)
ts.append(attribExpr(l.head, env, pt));
return ts.toList();
}
/** Attribute a list of statements, returning nothing.
*/
void attribStats(List trees, Env env) {
for (List l = trees; l.nonEmpty(); l = l.tail)
attribStat(l.head, env);
}
/** Attribute the arguments in a method call, returning a list of types.
*/
List attribArgs(List trees, Env env) {
ListBuffer argtypes = new ListBuffer();
for (List l = trees; l.nonEmpty(); l = l.tail)
argtypes.append(chk.checkNonVoid(
l.head.pos(), types.upperBound(attribTree(l.head, env, VAL, Infer.anyPoly))));
return argtypes.toList();
}
/** Attribute a type argument list, returning a list of types.
* Caller is responsible for calling checkRefTypes.
*/
List attribAnyTypes(List trees, Env env) {
ListBuffer argtypes = new ListBuffer();
for (List l = trees; l.nonEmpty(); l = l.tail)
argtypes.append(attribType(l.head, env));
return argtypes.toList();
}
/** Attribute a type argument list, returning a list of types.
* Check that all the types are references.
*/
List attribTypes(List trees, Env env) {
List types = attribAnyTypes(trees, env);
return chk.checkRefTypes(trees, types);
}
/**
* Attribute type variables (of generic classes or methods).
* Compound types are attributed later in attribBounds.
* @param typarams the type variables to enter
* @param env the current environment
*/
void attribTypeVariables(List typarams, Env env) {
for (JCTypeParameter tvar : typarams) {
TypeVar a = (TypeVar)tvar.type;
a.tsym.flags_field |= UNATTRIBUTED;
a.bound = Type.noType;
if (!tvar.bounds.isEmpty()) {
List bounds = List.of(attribType(tvar.bounds.head, env));
for (JCExpression bound : tvar.bounds.tail)
bounds = bounds.prepend(attribType(bound, env));
types.setBounds(a, bounds.reverse());
} else {
// if no bounds are given, assume a single bound of
// java.lang.Object.
types.setBounds(a, List.of(syms.objectType));
}
a.tsym.flags_field &= ~UNATTRIBUTED;
}
for (JCTypeParameter tvar : typarams)
chk.checkNonCyclic(tvar.pos(), (TypeVar)tvar.type);
attribStats(typarams, env);
}
void attribBounds(List typarams) {
for (JCTypeParameter typaram : typarams) {
Type bound = typaram.type.getUpperBound();
if (bound != null && bound.tsym instanceof ClassSymbol) {
ClassSymbol c = (ClassSymbol)bound.tsym;
if ((c.flags_field & COMPOUND) != 0) {
Assert.check((c.flags_field & UNATTRIBUTED) != 0, c);
attribClass(typaram.pos(), c);
}
}
}
}
/**
* Attribute the type references in a list of annotations.
*/
void attribAnnotationTypes(List annotations,
Env env) {
for (List al = annotations; al.nonEmpty(); al = al.tail) {
JCAnnotation a = al.head;
attribType(a.annotationType, env);
}
}
/**
* Attribute a "lazy constant value".
* @param env The env for the const value
* @param initializer The initializer for the const value
* @param type The expected type, or null
* @see VarSymbol#setlazyConstValue
*/
public Object attribLazyConstantValue(Env env,
JCTree.JCExpression initializer,
Type type) {
// in case no lint value has been set up for this env, scan up
// env stack looking for smallest enclosing env for which it is set.
Env lintEnv = env;
while (lintEnv.info.lint == null)
lintEnv = lintEnv.next;
// Having found the enclosing lint value, we can initialize the lint value for this class
// ... but ...
// There's a problem with evaluating annotations in the right order, such that
// env.info.enclVar.attributes_field might not yet have been evaluated, and so might be
// null. In that case, calling augment will throw an NPE. To avoid this, for now we
// revert to the jdk 6 behavior and ignore the (unevaluated) attributes.
if (env.info.enclVar.attributes_field == null)
env.info.lint = lintEnv.info.lint;
else
env.info.lint = lintEnv.info.lint.augment(env.info.enclVar.attributes_field, env.info.enclVar.flags());
Lint prevLint = chk.setLint(env.info.lint);
JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile);
try {
Type itype = attribExpr(initializer, env, type);
if (itype.constValue() != null)
return coerce(itype, type).constValue();
else
return null;
} finally {
env.info.lint = prevLint;
log.useSource(prevSource);
}
}
/** Attribute type reference in an `extends' or `implements' clause.
* Supertypes of anonymous inner classes are usually already attributed.
*
* @param tree The tree making up the type reference.
* @param env The environment current at the reference.
* @param classExpected true if only a class is expected here.
* @param interfaceExpected true if only an interface is expected here.
*/
Type attribBase(JCTree tree,
Env env,
boolean classExpected,
boolean interfaceExpected,
boolean checkExtensible) {
Type t = tree.type != null ?
tree.type :
attribType(tree, env);
return checkBase(t, tree, env, classExpected, interfaceExpected, checkExtensible);
}
Type checkBase(Type t,
JCTree tree,
Env env,
boolean classExpected,
boolean interfaceExpected,
boolean checkExtensible) {
if (t.isErroneous())
return t;
if (t.tag == TYPEVAR && !classExpected && !interfaceExpected) {
// check that type variable is already visible
if (t.getUpperBound() == null) {
log.error(tree.pos(), "illegal.forward.ref");
return types.createErrorType(t);
}
} else {
t = chk.checkClassType(tree.pos(), t, checkExtensible|!allowGenerics);
}
if (interfaceExpected && (t.tsym.flags() & INTERFACE) == 0) {
log.error(tree.pos(), "intf.expected.here");
// return errType is necessary since otherwise there might
// be undetected cycles which cause attribution to loop
return types.createErrorType(t);
} else if (checkExtensible &&
classExpected &&
(t.tsym.flags() & INTERFACE) != 0) {
log.error(tree.pos(), "no.intf.expected.here");
return types.createErrorType(t);
}
if (checkExtensible &&
((t.tsym.flags() & FINAL) != 0)) {
log.error(tree.pos(),
"cant.inherit.from.final", t.tsym);
}
chk.checkNonCyclic(tree.pos(), t);
return t;
}
Type attribIdentAsEnumType(Env env, JCIdent id) {
Assert.check((env.enclClass.sym.flags() & ENUM) != 0);
id.type = env.info.scope.owner.type;
id.sym = env.info.scope.owner;
return id.type;
}
public void visitClassDef(JCClassDecl tree) {
// Local classes have not been entered yet, so we need to do it now:
if ((env.info.scope.owner.kind & (VAR | MTH)) != 0)
enter.classEnter(tree, env);
ClassSymbol c = tree.sym;
if (c == null) {
// exit in case something drastic went wrong during enter.
result = null;
} else {
// make sure class has been completed:
c.complete();
// If this class appears as an anonymous class
// in a superclass constructor call where
// no explicit outer instance is given,
// disable implicit outer instance from being passed.
// (This would be an illegal access to "this before super").
if (env.info.isSelfCall &&
env.tree.getTag() == JCTree.NEWCLASS &&
((JCNewClass) env.tree).encl == null)
{
c.flags_field |= NOOUTERTHIS;
}
attribClass(tree.pos(), c);
result = tree.type = c.type;
}
}
public void visitMethodDef(JCMethodDecl tree) {
MethodSymbol m = tree.sym;
Lint lint = env.info.lint.augment(m.attributes_field, m.flags());
Lint prevLint = chk.setLint(lint);
MethodSymbol prevMethod = chk.setMethod(m);
try {
deferredLintHandler.flush(tree.pos());
chk.checkDeprecatedAnnotation(tree.pos(), m);
attribBounds(tree.typarams);
// If we override any other methods, check that we do so properly.
// JLS ???
if (m.isStatic()) {
chk.checkHideClashes(tree.pos(), env.enclClass.type, m);
} else {
chk.checkOverrideClashes(tree.pos(), env.enclClass.type, m);
}
chk.checkOverride(tree, m);
// Create a new environment with local scope
// for attributing the method.
Env localEnv = memberEnter.methodEnv(tree, env);
localEnv.info.lint = lint;
// Enter all type parameters into the local method scope.
for (List l = tree.typarams; l.nonEmpty(); l = l.tail)
localEnv.info.scope.enterIfAbsent(l.head.type.tsym);
ClassSymbol owner = env.enclClass.sym;
if ((owner.flags() & ANNOTATION) != 0 &&
tree.params.nonEmpty())
log.error(tree.params.head.pos(),
"intf.annotation.members.cant.have.params");
// Attribute all value parameters.
for (List l = tree.params; l.nonEmpty(); l = l.tail) {
attribStat(l.head, localEnv);
}
chk.checkVarargsMethodDecl(localEnv, tree);
// Check that type parameters are well-formed.
chk.validate(tree.typarams, localEnv);
// Check that result type is well-formed.
chk.validate(tree.restype, localEnv);
// annotation method checks
if ((owner.flags() & ANNOTATION) != 0) {
// annotation method cannot have throws clause
if (tree.thrown.nonEmpty()) {
log.error(tree.thrown.head.pos(),
"throws.not.allowed.in.intf.annotation");
}
// annotation method cannot declare type-parameters
if (tree.typarams.nonEmpty()) {
log.error(tree.typarams.head.pos(),
"intf.annotation.members.cant.have.type.params");
}
// validate annotation method's return type (could be an annotation type)
chk.validateAnnotationType(tree.restype);
// ensure that annotation method does not clash with members of Object/Annotation
chk.validateAnnotationMethod(tree.pos(), m);
if (tree.defaultValue != null) {
// if default value is an annotation, check it is a well-formed
// annotation value (e.g. no duplicate values, no missing values, etc.)
chk.validateAnnotationTree(tree.defaultValue);
}
}
for (List l = tree.thrown; l.nonEmpty(); l = l.tail)
chk.checkType(l.head.pos(), l.head.type, syms.throwableType);
if (tree.body == null) {
// Empty bodies are only allowed for
// abstract, native, or interface methods, or for methods
// in a retrofit signature class.
if ((owner.flags() & INTERFACE) == 0 &&
(tree.mods.flags & (ABSTRACT | NATIVE)) == 0 &&
!relax)
log.error(tree.pos(), "missing.meth.body.or.decl.abstract");
if (tree.defaultValue != null) {
if ((owner.flags() & ANNOTATION) == 0)
log.error(tree.pos(),
"default.allowed.in.intf.annotation.member");
}
} else if ((owner.flags() & INTERFACE) != 0) {
log.error(tree.body.pos(), "intf.meth.cant.have.body");
} else if ((tree.mods.flags & ABSTRACT) != 0) {
log.error(tree.pos(), "abstract.meth.cant.have.body");
} else if ((tree.mods.flags & NATIVE) != 0) {
log.error(tree.pos(), "native.meth.cant.have.body");
} else {
// Add an implicit super() call unless an explicit call to
// super(...) or this(...) is given
// or we are compiling class java.lang.Object.
if (tree.name == names.init && owner.type != syms.objectType) {
JCBlock body = tree.body;
if (body.stats.isEmpty() ||
!TreeInfo.isSelfCall(body.stats.head)) {
body.stats = body.stats.
prepend(memberEnter.SuperCall(make.at(body.pos),
List.nil(),
List.nil(),
false));
} else if ((env.enclClass.sym.flags() & ENUM) != 0 &&
(tree.mods.flags & GENERATEDCONSTR) == 0 &&
TreeInfo.isSuperCall(body.stats.head)) {
// enum constructors are not allowed to call super
// directly, so make sure there aren't any super calls
// in enum constructors, except in the compiler
// generated one.
log.error(tree.body.stats.head.pos(),
"call.to.super.not.allowed.in.enum.ctor",
env.enclClass.sym);
}
}
// Attribute method body.
attribStat(tree.body, localEnv);
}
localEnv.info.scope.leave();
result = tree.type = m.type;
chk.validateAnnotations(tree.mods.annotations, m);
}
finally {
chk.setLint(prevLint);
chk.setMethod(prevMethod);
}
}
public void visitVarDef(JCVariableDecl tree) {
// Local variables have not been entered yet, so we need to do it now:
if (env.info.scope.owner.kind == MTH) {
if (tree.sym != null) {
// parameters have already been entered
env.info.scope.enter(tree.sym);
} else {
memberEnter.memberEnter(tree, env);
annotate.flush();
}
tree.sym.flags_field |= EFFECTIVELY_FINAL;
}
VarSymbol v = tree.sym;
Lint lint = env.info.lint.augment(v.attributes_field, v.flags());
Lint prevLint = chk.setLint(lint);
// Check that the variable's declared type is well-formed.
chk.validate(tree.vartype, env);
deferredLintHandler.flush(tree.pos());
try {
chk.checkDeprecatedAnnotation(tree.pos(), v);
if (tree.init != null) {
if ((v.flags_field & FINAL) != 0 && tree.init.getTag() != JCTree.NEWCLASS) {
// In this case, `v' is final. Ensure that it's initializer is
// evaluated.
v.getConstValue(); // ensure initializer is evaluated
} else {
// Attribute initializer in a new environment
// with the declared variable as owner.
// Check that initializer conforms to variable's declared type.
Env initEnv = memberEnter.initEnv(tree, env);
initEnv.info.lint = lint;
// In order to catch self-references, we set the variable's
// declaration position to maximal possible value, effectively
// marking the variable as undefined.
initEnv.info.enclVar = v;
attribExpr(tree.init, initEnv, v.type);
}
}
result = tree.type = v.type;
chk.validateAnnotations(tree.mods.annotations, v);
}
finally {
chk.setLint(prevLint);
}
}
public void visitSkip(JCSkip tree) {
result = null;
}
public void visitBlock(JCBlock tree) {
if (env.info.scope.owner.kind == TYP) {
// Block is a static or instance initializer;
// let the owner of the environment be a freshly
// created BLOCK-method.
Env localEnv =
env.dup(tree, env.info.dup(env.info.scope.dupUnshared()));
localEnv.info.scope.owner =
new MethodSymbol(tree.flags | BLOCK, names.empty, null,
env.info.scope.owner);
if ((tree.flags & STATIC) != 0) localEnv.info.staticLevel++;
attribStats(tree.stats, localEnv);
} else {
// Create a new local environment with a local scope.
Env localEnv =
env.dup(tree, env.info.dup(env.info.scope.dup()));
attribStats(tree.stats, localEnv);
localEnv.info.scope.leave();
}
result = null;
}
public void visitDoLoop(JCDoWhileLoop tree) {
attribStat(tree.body, env.dup(tree));
attribExpr(tree.cond, env, syms.booleanType);
result = null;
}
public void visitWhileLoop(JCWhileLoop tree) {
attribExpr(tree.cond, env, syms.booleanType);
attribStat(tree.body, env.dup(tree));
result = null;
}
public void visitForLoop(JCForLoop tree) {
Env loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
attribStats(tree.init, loopEnv);
if (tree.cond != null) attribExpr(tree.cond, loopEnv, syms.booleanType);
loopEnv.tree = tree; // before, we were not in loop!
attribStats(tree.step, loopEnv);
attribStat(tree.body, loopEnv);
loopEnv.info.scope.leave();
result = null;
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
Env loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
attribStat(tree.var, loopEnv);
Type exprType = types.upperBound(attribExpr(tree.expr, loopEnv));
chk.checkNonVoid(tree.pos(), exprType);
Type elemtype = types.elemtype(exprType); // perhaps expr is an array?
if (elemtype == null) {
// or perhaps expr implements Iterable?
Type base = types.asSuper(exprType, syms.iterableType.tsym);
if (base == null) {
log.error(tree.expr.pos(),
"foreach.not.applicable.to.type",
exprType,
diags.fragment("type.req.array.or.iterable"));
elemtype = types.createErrorType(exprType);
} else {
List iterableParams = base.allparams();
elemtype = iterableParams.isEmpty()
? syms.objectType
: types.upperBound(iterableParams.head);
}
}
chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type);
loopEnv.tree = tree; // before, we were not in loop!
attribStat(tree.body, loopEnv);
loopEnv.info.scope.leave();
result = null;
}
public void visitLabelled(JCLabeledStatement tree) {
// Check that label is not used in an enclosing statement
Env env1 = env;
while (env1 != null && env1.tree.getTag() != JCTree.CLASSDEF) {
if (env1.tree.getTag() == JCTree.LABELLED &&
((JCLabeledStatement) env1.tree).label == tree.label) {
log.error(tree.pos(), "label.already.in.use",
tree.label);
break;
}
env1 = env1.next;
}
attribStat(tree.body, env.dup(tree));
result = null;
}
public void visitSwitch(JCSwitch tree) {
Type seltype = attribExpr(tree.selector, env);
Env switchEnv =
env.dup(tree, env.info.dup(env.info.scope.dup()));
boolean enumSwitch =
allowEnums &&
(seltype.tsym.flags() & Flags.ENUM) != 0;
boolean stringSwitch = false;
if (types.isSameType(seltype, syms.stringType)) {
if (allowStringsInSwitch) {
stringSwitch = true;
} else {
log.error(tree.selector.pos(), "string.switch.not.supported.in.source", sourceName);
}
}
if (!enumSwitch && !stringSwitch)
seltype = chk.checkType(tree.selector.pos(), seltype, syms.intType);
// Attribute all cases and
// check that there are no duplicate case labels or default clauses.
Set