/*
* Copyright (c) 1999, 2013, 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 javax.lang.model.element.ElementKind;
import javax.lang.model.type.TypeKind;
import javax.tools.JavaFileObject;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.TreeVisitor;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.comp.Check.CheckContext;
import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
import com.sun.tools.javac.comp.Infer.InferenceContext;
import com.sun.tools.javac.comp.Infer.FreeTypeListener;
import com.sun.tools.javac.jvm.*;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Flags.ANNOTATION;
import static com.sun.tools.javac.code.Flags.BLOCK;
import static com.sun.tools.javac.code.Kinds.*;
import static com.sun.tools.javac.code.Kinds.ERRONEOUS;
import static com.sun.tools.javac.code.TypeTag.*;
import static com.sun.tools.javac.code.TypeTag.WILDCARD;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
/** 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 DeferredAttr deferredAttr;
final Check chk;
final Flow flow;
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);
flow = Flow.instance(context);
memberEnter = MemberEnter.instance(context);
make = TreeMaker.instance(context);
enter = Enter.instance(context);
infer = Infer.instance(context);
deferredAttr = DeferredAttr.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();
allowPoly = source.allowPoly();
allowLambda = source.allowLambda();
allowDefaultMethods = source.allowDefaultMethods();
sourceName = source.name;
relax = (options.isSet("-retrofit") ||
options.isSet("-relax"));
findDiamonds = options.get("findDiamond") != null &&
source.allowDiamond();
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
identifyLambdaCandidate = options.getBoolean("identifyLambdaCandidate", false);
statInfo = new ResultInfo(NIL, Type.noType);
varInfo = new ResultInfo(VAR, Type.noType);
unknownExprInfo = new ResultInfo(VAL, Type.noType);
unknownTypeInfo = new ResultInfo(TYP, Type.noType);
unknownTypeExprInfo = new ResultInfo(Kinds.TYP | Kinds.VAL, Type.noType);
recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext);
}
/** Switch: relax some constraints for retrofit mode.
*/
boolean relax;
/** Switch: support target-typing inference
*/
boolean allowPoly;
/** 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: support lambda expressions ?
*/
boolean allowLambda;
/** Switch: support default methods ?
*/
boolean allowDefaultMethods;
/** 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: generate warnings whenever an anonymous inner class that is convertible
* to a lambda expression is found
*/
boolean identifyLambdaCandidate;
/**
* 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 ownkind The computed kind of the tree
* @param resultInfo The expected result of the tree
*/
Type check(final JCTree tree, final Type found, final int ownkind, final ResultInfo resultInfo) {
InferenceContext inferenceContext = resultInfo.checkContext.inferenceContext();
Type owntype = found;
if (!owntype.hasTag(ERROR) && !resultInfo.pt.hasTag(METHOD) && !resultInfo.pt.hasTag(FORALL)) {
if (inferenceContext.free(found)) {
inferenceContext.addFreeTypeListener(List.of(found, resultInfo.pt), new FreeTypeListener() {
@Override
public void typesInferred(InferenceContext inferenceContext) {
ResultInfo pendingResult =
resultInfo.dup(inferenceContext.asInstType(resultInfo.pt));
check(tree, inferenceContext.asInstType(found), ownkind, pendingResult);
}
});
return tree.type = resultInfo.pt;
} else {
if ((ownkind & ~resultInfo.pkind) == 0) {
owntype = resultInfo.check(tree, owntype);
} else {
log.error(tree.pos(), "unexpected.type",
kindNames(resultInfo.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 = owner(env);
// 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));
}
/**
* Return the innermost enclosing owner symbol in a given attribution context
*/
Symbol owner(Env env) {
while (true) {
switch (env.tree.getTag()) {
case VARDEF:
//a field can be owner
VarSymbol vsym = ((JCVariableDecl)env.tree).sym;
if (vsym.owner.kind == TYP) {
return vsym;
}
break;
case METHODDEF:
//method def is always an owner
return ((JCMethodDecl)env.tree).sym;
case CLASSDEF:
//class def is always an owner
return ((JCClassDecl)env.tree).sym;
case LAMBDA:
//a lambda is an owner - return a fresh synthetic method symbol
return new MethodSymbol(0, names.empty, null, syms.methodClass);
case BLOCK:
//static/instance init blocks are owner
Symbol blockSym = env.info.scope.owner;
if ((blockSym.flags() & BLOCK) != 0) {
return blockSym;
}
break;
case TOPLEVEL:
//toplevel is always an owner (for pkge decls)
return env.info.scope.owner;
}
Assert.checkNonNull(env.next);
env = env.next;
}
}
/** 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.hasTag(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);
}
}
}
/** 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.hasTag(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, unknownTypeInfo);
}
public Type attribImportQualifier(JCImport tree, Env env) {
// Attribute qualifying package or class.
JCFieldAccess s = (JCFieldAccess)tree.qualid;
return attribTree(s.selected,
env,
new ResultInfo(tree.staticImport ? TYP : (TYP | PCK),
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 = copyEnv(env);
}
private Env copyEnv(Env env) {
Env newEnv =
env.dup(env.tree, env.info.dup(copyScope(env.info.scope)));
if (newEnv.outer != null) {
newEnv.outer = copyEnv(newEnv.outer);
}
return newEnv;
}
private Scope copyScope(Scope sc) {
Scope newScope = new Scope(sc.owner);
List elemsList = List.nil();
while (sc != null) {
for (Scope.Entry e = sc.elems ; e != null ; e = e.sibling) {
elemsList = elemsList.prepend(e.sym);
}
sc = sc.next;
}
for (Symbol s : elemsList) {
newScope.enter(s);
}
return newScope;
}
}
class ResultInfo {
final int pkind;
final Type pt;
final CheckContext checkContext;
ResultInfo(int pkind, Type pt) {
this(pkind, pt, chk.basicHandler);
}
protected ResultInfo(int pkind, Type pt, CheckContext checkContext) {
this.pkind = pkind;
this.pt = pt;
this.checkContext = checkContext;
}
protected Type check(final DiagnosticPosition pos, final Type found) {
return chk.checkType(pos, found, pt, checkContext);
}
protected ResultInfo dup(Type newPt) {
return new ResultInfo(pkind, newPt, checkContext);
}
protected ResultInfo dup(CheckContext newContext) {
return new ResultInfo(pkind, pt, newContext);
}
}
class RecoveryInfo extends ResultInfo {
public RecoveryInfo(final DeferredAttr.DeferredAttrContext deferredAttrContext) {
super(Kinds.VAL, Type.recoveryType, new Check.NestedCheckContext(chk.basicHandler) {
@Override
public DeferredAttr.DeferredAttrContext deferredAttrContext() {
return deferredAttrContext;
}
@Override
public boolean compatible(Type found, Type req, Warner warn) {
return true;
}
@Override
public void report(DiagnosticPosition pos, JCDiagnostic details) {
chk.basicHandler.report(pos, details);
}
});
}
@Override
protected Type check(DiagnosticPosition pos, Type found) {
return chk.checkNonVoid(pos, super.check(pos, found));
}
}
final ResultInfo statInfo;
final ResultInfo varInfo;
final ResultInfo unknownExprInfo;
final ResultInfo unknownTypeInfo;
final ResultInfo unknownTypeExprInfo;
final ResultInfo recoveryInfo;
Type pt() {
return resultInfo.pt;
}
int pkind() {
return resultInfo.pkind;
}
/* ************************************************************************
* Visitor methods
*************************************************************************/
/** Visitor argument: the current environment.
*/
Env env;
/** Visitor argument: the currently expected attribution result.
*/
ResultInfo resultInfo;
/** 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 resultInfo The result info visitor argument.
*/
Type attribTree(JCTree tree, Env env, ResultInfo resultInfo) {
Env prevEnv = this.env;
ResultInfo prevResult = this.resultInfo;
try {
this.env = env;
this.resultInfo = resultInfo;
tree.accept(this);
if (tree == breakTree &&
resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) {
throw new BreakAttr(env);
}
return result;
} catch (CompletionFailure ex) {
tree.type = syms.errType;
return chk.completionError(tree.pos(), ex);
} finally {
this.env = prevEnv;
this.resultInfo = prevResult;
}
}
/** Derived visitor method: attribute an expression tree.
*/
public Type attribExpr(JCTree tree, Env env, Type pt) {
return attribTree(tree, env, new ResultInfo(VAL, !pt.hasTag(ERROR) ? pt : Type.noType));
}
/** Derived visitor method: attribute an expression tree with
* no constraints on the computed type.
*/
public Type attribExpr(JCTree tree, Env env) {
return attribTree(tree, env, unknownExprInfo);
}
/** Derived visitor method: attribute a type tree.
*/
public 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, new ResultInfo(TYP, pt));
return result;
}
/** Derived visitor method: attribute a statement or definition tree.
*/
public Type attribStat(JCTree tree, Env env) {
return attribTree(tree, env, statInfo);
}
/** 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 (JCExpression arg : trees) {
Type argtype = allowPoly && deferredAttr.isDeferred(env, arg) ?
deferredAttr.new DeferredType(arg, env) :
chk.checkNonVoid(arg, attribExpr(arg, env, Infer.anyPoly));
argtypes.append(argtype);
}
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);
}
}
/**
* 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.annotations.pendingCompletion()) {
env.info.lint = lintEnv.info.lint;
} else {
env.info.lint = lintEnv.info.lint.augment(env.info.enclVar.annotations,
env.info.enclVar.flags());
}
Lint prevLint = chk.setLint(env.info.lint);
JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile);
try {
// Use null as symbol to not attach the type annotation to any symbol.
// The initializer will later also be visited and then we'll attach
// to the symbol.
// This prevents having multiple type annotations, just because of
// lazy constant value evaluation.
memberEnter.typeAnnotate(initializer, env, null);
annotate.flush();
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.hasTag(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.hasTag(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;
boolean isDefaultMethod = (m.flags() & DEFAULT) != 0;
Lint lint = env.info.lint.augment(m.annotations, m.flags());
Lint prevLint = chk.setLint(lint);
MethodSymbol prevMethod = chk.setMethod(m);
try {
deferredLintHandler.flush(tree.pos());
chk.checkDeprecatedAnnotation(tree.pos(), m);
// Create a new environment with local scope
// for attributing the method.
Env localEnv = memberEnter.methodEnv(tree, env);
localEnv.info.lint = lint;
attribStats(tree.typarams, localEnv);
// 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);
if (isDefaultMethod && types.overridesObjectMethod(m.enclClass(), m)) {
log.error(tree, "default.overrides.object.member", m.name, Kinds.kindName(m.location()), m.location());
}
// 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);
// Check that receiver type is well-formed.
if (tree.recvparam != null) {
// Use a new environment to check the receiver parameter.
// Otherwise I get "might not have been initialized" errors.
// Is there a better way?
Env newEnv = memberEnter.methodEnv(tree, env);
attribType(tree.recvparam, newEnv);
chk.validate(tree.recvparam, newEnv);
}
// 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 (isDefaultMethod || (tree.sym.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 ((tree.sym.flags() & ABSTRACT) != 0 && !isDefaultMethod) {
if ((owner.flags() & INTERFACE) != 0) {
log.error(tree.body.pos(), "intf.meth.cant.have.body");
} else {
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 all type annotations in the body
memberEnter.typeAnnotate(tree.body, localEnv, m);
annotate.flush();
// 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();
}
} else {
if (tree.init != null) {
// Field initializer expression need to be entered.
memberEnter.typeAnnotate(tree.init, env, tree.sym);
annotate.flush();
}
}
VarSymbol v = tree.sym;
Lint lint = env.info.lint.augment(v.annotations, v.flags());
Lint prevLint = chk.setLint(lint);
// Check that the variable's declared type is well-formed.
boolean isImplicitLambdaParameter = env.tree.hasTag(LAMBDA) &&
((JCLambda)env.tree).paramKind == JCLambda.ParameterKind.IMPLICIT &&
(tree.sym.flags() & PARAMETER) != 0;
chk.validate(tree.vartype, env, !isImplicitLambdaParameter);
deferredLintHandler.flush(tree.pos());
try {
chk.checkDeprecatedAnnotation(tree.pos(), v);
if (tree.init != null) {
if ((v.flags_field & FINAL) != 0 &&
!tree.init.hasTag(NEWCLASS) &&
!tree.init.hasTag(LAMBDA) &&
!tree.init.hasTag(REFERENCE)) {
// 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 |
env.info.scope.owner.flags() & STRICTFP, names.empty, null,
env.info.scope.owner);
if ((tree.flags & STATIC) != 0) localEnv.info.staticLevel++;
// Attribute all type annotations in the block
memberEnter.typeAnnotate(tree, localEnv, localEnv.info.scope.owner);
annotate.flush();
{
// Store init and clinit type annotations with the ClassSymbol
// to allow output in Gen.normalizeDefs.
ClassSymbol cs = (ClassSymbol)env.info.scope.owner;
List tas = localEnv.info.scope.owner.getRawTypeAttributes();
if ((tree.flags & STATIC) != 0) {
cs.annotations.appendClassInitTypeAttributes(tas);
} else {
cs.annotations.appendInitTypeAttributes(tas);
}
}
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()));
try {
attribStats(tree.stats, localEnv);
} finally {
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()));
try {
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);
result = null;
}
finally {
loopEnv.info.scope.leave();
}
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
Env loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
try {
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);
result = null;
}
finally {
loopEnv.info.scope.leave();
}
}
public void visitLabelled(JCLabeledStatement tree) {
// Check that label is not used in an enclosing statement
Env env1 = env;
while (env1 != null && !env1.tree.hasTag(CLASSDEF)) {
if (env1.tree.hasTag(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()));
try {
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