Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 1999, 2021, 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.function.BiConsumer;
import java.util.stream.Collectors;
import javax.lang.model.element.ElementKind;
import javax.tools.JavaFileObject;
import com.sun.source.tree.CaseTree;
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.Scope.WriteableScope;
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.code.TypeMetadata.Annotations;
import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext;
import com.sun.tools.javac.comp.Check.CheckContext;
import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
import com.sun.tools.javac.comp.MatchBindingsComputer.MatchBindings;
import com.sun.tools.javac.jvm.*;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Diamond;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArg;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArgs;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import com.sun.tools.javac.resources.CompilerProperties.Warnings;
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.DefinedBy.Api;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.JCDiagnostic.Error;
import com.sun.tools.javac.util.JCDiagnostic.Fragment;
import com.sun.tools.javac.util.JCDiagnostic.Warning;
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.Kind.*;
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.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
/** 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 Operators operators;
final Infer infer;
final Analyzer analyzer;
final DeferredAttr deferredAttr;
final Check chk;
final ExtraSymbolInfo esi;
final Flow flow;
final MemberEnter memberEnter;
final TypeEnter typeEnter;
final TreeMaker make;
final ConstFold cfolder;
final Enter enter;
final Target target;
final Types types;
final Preview preview;
final JCDiagnostic.Factory diags;
final TypeAnnotations typeAnnotations;
final DeferredLintHandler deferredLintHandler;
final TypeEnvs typeEnvs;
final Dependencies dependencies;
final Annotate annotate;
final ArgumentAttr argumentAttr;
final MatchBindingsComputer matchBindingsComputer;
final AttrRecover attrRecover;
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);
operators = Operators.instance(context);
chk = Check.instance(context);
esi = ExtraSymbolInfo.instance(context);
flow = Flow.instance(context);
memberEnter = MemberEnter.instance(context);
typeEnter = TypeEnter.instance(context);
make = TreeMaker.instance(context);
enter = Enter.instance(context);
infer = Infer.instance(context);
analyzer = Analyzer.instance(context);
deferredAttr = DeferredAttr.instance(context);
cfolder = ConstFold.instance(context);
target = Target.instance(context);
types = Types.instance(context);
preview = Preview.instance(context);
diags = JCDiagnostic.Factory.instance(context);
annotate = Annotate.instance(context);
typeAnnotations = TypeAnnotations.instance(context);
deferredLintHandler = DeferredLintHandler.instance(context);
typeEnvs = TypeEnvs.instance(context);
dependencies = Dependencies.instance(context);
argumentAttr = ArgumentAttr.instance(context);
matchBindingsComputer = MatchBindingsComputer.instance(context);
attrRecover = AttrRecover.instance(context);
Options options = Options.instance(context);
Source source = Source.instance(context);
Target target = Target.instance(context);
allowPoly = Feature.POLY.allowedInSource(source, target);
allowTypeAnnos = Feature.TYPE_ANNOTATIONS.allowedInSource(source, target);
allowLambda = Feature.LAMBDA.allowedInSource(source, target);
allowDefaultMethods = Feature.DEFAULT_METHODS.allowedInSource(source, target);
allowStaticInterfaceMethods = Feature.STATIC_INTERFACE_METHODS.allowedInSource(source, target);
allowReifiableTypesInInstanceof =
Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source, target) &&
(!preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF) || preview.isEnabled());
allowRecords = Feature.RECORDS.allowedInSource(source, target);
sourceName = source.name;
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
statInfo = new ResultInfo(KindSelector.NIL, Type.noType);
varAssignmentInfo = new ResultInfo(KindSelector.ASG, Type.noType);
unknownExprInfo = new ResultInfo(KindSelector.VAL, Type.noType);
methodAttrInfo = new MethodAttrInfo();
unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType);
unknownTypeExprInfo = new ResultInfo(KindSelector.VAL_TYP, Type.noType);
recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext);
}
/** Switch: support target-typing inference
*/
boolean allowPoly;
/** Switch: support type annotations.
*/
boolean allowTypeAnnos;
/** Switch: support lambda expressions ?
*/
boolean allowLambda;
/** Switch: support default methods ?
*/
boolean allowDefaultMethods;
/** Switch: static interface methods enabled?
*/
boolean allowStaticInterfaceMethods;
/** Switch: reifiable types in instanceof enabled?
*/
boolean allowReifiableTypesInInstanceof;
/** Are records allowed
*/
private final boolean allowRecords;
/**
* Switch: warn about use of variable before declaration?
* RFE: 6425594
*/
boolean useBeforeDeclarationWarning;
/**
* 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 found The computed type of the tree
* @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 KindSelector ownkind,
final ResultInfo resultInfo) {
InferenceContext inferenceContext = resultInfo.checkContext.inferenceContext();
Type owntype;
boolean shouldCheck = !found.hasTag(ERROR) &&
!resultInfo.pt.hasTag(METHOD) &&
!resultInfo.pt.hasTag(FORALL);
if (shouldCheck && !ownkind.subset(resultInfo.pkind)) {
log.error(tree.pos(),
Errors.UnexpectedType(resultInfo.pkind.kindNames(),
ownkind.kindNames()));
owntype = types.createErrorType(found);
} else if (allowPoly && inferenceContext.free(found)) {
//delay the check if there are inference variables in the found type
//this means we are dealing with a partially inferred poly expression
owntype = shouldCheck ? resultInfo.pt : found;
if (resultInfo.checkMode.installPostInferenceHook()) {
inferenceContext.addFreeTypeListener(List.of(found),
instantiatedContext -> {
ResultInfo pendingResult =
resultInfo.dup(inferenceContext.asInstType(resultInfo.pt));
check(tree, inferenceContext.asInstType(found), ownkind, pendingResult);
});
}
} else {
owntype = shouldCheck ?
resultInfo.check(tree, found) :
found;
}
if (resultInfo.checkMode.updateTreeType()) {
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.
boolean isAssignable =
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));
boolean insideCompactConstructor = env.enclMethod != null && TreeInfo.isCompactConstructor(env.enclMethod);
return isAssignable & !insideCompactConstructor;
}
/** Check that variable can be assigned to.
* @param pos The current source code position.
* @param v The assigned variable
* @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.name == names._this) {
log.error(pos, Errors.CantAssignValToThis);
} else if ((v.flags() & FINAL) != 0 &&
((v.flags() & HASINIT) != 0
||
!((base == null ||
TreeInfo.isThisQualifier(base)) &&
isAssignableAsBlankFinal(v, env)))) {
if (v.isResourceVariable()) { //TWR resource
log.error(pos, Errors.TryResourceMayNotBeAssigned(v));
} else {
log.error(pos, Errors.CantAssignValToFinalVar(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 attribIdent(tree, localEnv);
}
/** Attribute a parsed identifier.
* @param tree Parsed identifier name
* @param env The env to use
*/
public Symbol attribIdent(JCTree tree, Env env) {
return tree.accept(identAttributer, env);
}
// where
private TreeVisitor> identAttributer = new IdentAttributer();
private class IdentAttributer extends SimpleTreeVisitor> {
@Override @DefinedBy(Api.COMPILER_TREE)
public Symbol visitMemberSelect(MemberSelectTree node, Env env) {
Symbol site = visit(node.getExpression(), env);
if (site.kind == ERR || site.kind == ABSENT_TYP || site.kind == HIDDEN)
return site;
Name name = (Name)node.getIdentifier();
if (site.kind == PCK) {
env.toplevel.packge = (PackageSymbol)site;
return rs.findIdentInPackage(null, env, (TypeSymbol)site, name,
KindSelector.TYP_PCK);
} else {
env.enclClass.sym = (ClassSymbol)site;
return rs.findMemberType(env, site.asType(), name, (TypeSymbol)site);
}
}
@Override @DefinedBy(Api.COMPILER_TREE)
public Symbol visitIdentifier(IdentifierTree node, Env env) {
return rs.findIdent(null, env, (Name)node.getName(), KindSelector.TYP_PCK);
}
}
public Type coerce(Type etype, Type ttype) {
return cfolder.coerce(etype, ttype);
}
public Type attribType(JCTree node, TypeSymbol sym) {
Env env = 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 ?
KindSelector.TYP : KindSelector.TYP_PCK,
Type.noType));
}
public Env attribExprToTree(JCTree expr, Env env, JCTree tree) {
return attribToTree(expr, env, tree, unknownExprInfo);
}
public Env attribStatToTree(JCTree stmt, Env env, JCTree tree) {
return attribToTree(stmt, env, tree, statInfo);
}
private Env attribToTree(JCTree root, Env env, JCTree tree, ResultInfo resultInfo) {
breakTree = tree;
JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
try {
deferredAttr.attribSpeculative(root, env, resultInfo,
null, DeferredAttr.AttributionMode.ATTRIB_TO_TREE,
argumentAttr.withLocalCacheContext());
attrRecover.doRecovery();
} 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 transient Env env;
private BreakAttr(Env env) {
this.env = env;
}
}
/**
* Mode controlling behavior of Attr.Check
*/
enum CheckMode {
NORMAL,
/**
* Mode signalling 'fake check' - skip tree update. A side-effect of this mode is
* that the captured var cache in {@code InferenceContext} will be used in read-only
* mode when performing inference checks.
*/
NO_TREE_UPDATE {
@Override
public boolean updateTreeType() {
return false;
}
},
/**
* Mode signalling that caller will manage free types in tree decorations.
*/
NO_INFERENCE_HOOK {
@Override
public boolean installPostInferenceHook() {
return false;
}
};
public boolean updateTreeType() {
return true;
}
public boolean installPostInferenceHook() {
return true;
}
}
class ResultInfo {
final KindSelector pkind;
final Type pt;
final CheckContext checkContext;
final CheckMode checkMode;
ResultInfo(KindSelector pkind, Type pt) {
this(pkind, pt, chk.basicHandler, CheckMode.NORMAL);
}
ResultInfo(KindSelector pkind, Type pt, CheckMode checkMode) {
this(pkind, pt, chk.basicHandler, checkMode);
}
protected ResultInfo(KindSelector pkind,
Type pt, CheckContext checkContext) {
this(pkind, pt, checkContext, CheckMode.NORMAL);
}
protected ResultInfo(KindSelector pkind,
Type pt, CheckContext checkContext, CheckMode checkMode) {
this.pkind = pkind;
this.pt = pt;
this.checkContext = checkContext;
this.checkMode = checkMode;
}
/**
* Should {@link Attr#attribTree} use the {@ArgumentAttr} visitor instead of this one?
* @param tree The tree to be type-checked.
* @return true if {@ArgumentAttr} should be used.
*/
protected boolean needsArgumentAttr(JCTree tree) { return false; }
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, checkMode);
}
protected ResultInfo dup(CheckContext newContext) {
return new ResultInfo(pkind, pt, newContext, checkMode);
}
protected ResultInfo dup(Type newPt, CheckContext newContext) {
return new ResultInfo(pkind, newPt, newContext, checkMode);
}
protected ResultInfo dup(Type newPt, CheckContext newContext, CheckMode newMode) {
return new ResultInfo(pkind, newPt, newContext, newMode);
}
protected ResultInfo dup(CheckMode newMode) {
return new ResultInfo(pkind, pt, checkContext, newMode);
}
@Override
public String toString() {
if (pt != null) {
return pt.toString();
} else {
return "";
}
}
}
class MethodAttrInfo extends ResultInfo {
public MethodAttrInfo() {
this(chk.basicHandler);
}
public MethodAttrInfo(CheckContext checkContext) {
super(KindSelector.VAL, Infer.anyPoly, checkContext);
}
@Override
protected boolean needsArgumentAttr(JCTree tree) {
return true;
}
protected ResultInfo dup(Type newPt) {
throw new IllegalStateException();
}
protected ResultInfo dup(CheckContext newContext) {
return new MethodAttrInfo(newContext);
}
protected ResultInfo dup(Type newPt, CheckContext newContext) {
throw new IllegalStateException();
}
protected ResultInfo dup(Type newPt, CheckContext newContext, CheckMode newMode) {
throw new IllegalStateException();
}
protected ResultInfo dup(CheckMode newMode) {
throw new IllegalStateException();
}
}
class RecoveryInfo extends ResultInfo {
public RecoveryInfo(final DeferredAttr.DeferredAttrContext deferredAttrContext) {
this(deferredAttrContext, Type.recoveryType);
}
public RecoveryInfo(final DeferredAttr.DeferredAttrContext deferredAttrContext, Type pt) {
super(KindSelector.VAL, pt, 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) {
if (pt == Type.recoveryType) {
chk.basicHandler.report(pos, details);
}
}
});
}
}
final ResultInfo statInfo;
final ResultInfo varAssignmentInfo;
final ResultInfo methodAttrInfo;
final ResultInfo unknownExprInfo;
final ResultInfo unknownTypeInfo;
final ResultInfo unknownTypeExprInfo;
final ResultInfo recoveryInfo;
Type pt() {
return resultInfo.pt;
}
KindSelector 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;
MatchBindings matchBindings = MatchBindingsComputer.EMPTY;
/** 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;
if (resultInfo.needsArgumentAttr(tree)) {
result = argumentAttr.attribArg(tree, env);
} else {
tree.accept(this);
}
matchBindings = matchBindingsComputer.finishBindings(tree,
matchBindings);
if (tree == breakTree &&
resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) {
breakTreeFound(copyEnv(env));
}
return result;
} catch (CompletionFailure ex) {
tree.type = syms.errType;
return chk.completionError(tree.pos(), ex);
} finally {
this.env = prevEnv;
this.resultInfo = prevResult;
}
}
protected void breakTreeFound(Env env) {
throw new BreakAttr(env);
}
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;
}
WriteableScope copyScope(WriteableScope sc) {
WriteableScope newScope = WriteableScope.create(sc.owner);
List elemsList = List.nil();
for (Symbol sym : sc.getSymbols()) {
elemsList = elemsList.prepend(sym);
}
for (Symbol s : elemsList) {
newScope.enter(s);
}
return newScope;
}
/** Derived visitor method: attribute an expression tree.
*/
public Type attribExpr(JCTree tree, Env env, Type pt) {
return attribTree(tree, env, new ResultInfo(KindSelector.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(KindSelector.TYP, pt));
return result;
}
/** Derived visitor method: attribute a statement or definition tree.
*/
public Type attribStat(JCTree tree, Env env) {
Env analyzeEnv = analyzer.copyEnvIfNeeded(tree, env);
Type result = attribTree(tree, env, statInfo);
analyzer.analyzeIfNeeded(tree, analyzeEnv);
attrRecover.doRecovery();
return result;
}
/** 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 the method kind.
*/
KindSelector attribArgs(KindSelector initialKind, List trees, Env env, ListBuffer argtypes) {
KindSelector kind = initialKind;
for (JCExpression arg : trees) {
Type argtype = chk.checkNonVoid(arg, attribTree(arg, env, allowPoly ? methodAttrInfo : unknownExprInfo));
if (argtype.hasTag(DEFERRED)) {
kind = KindSelector.of(KindSelector.POLY, kind);
}
argtypes.append(argtype);
}
return kind;
}
/** 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, boolean checkCyclic) {
for (JCTypeParameter tvar : typarams) {
TypeVar a = (TypeVar)tvar.type;
a.tsym.flags_field |= UNATTRIBUTED;
a.setUpperBound(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;
}
if (checkCyclic) {
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 variable The initializer for the const value
* @param type The expected type, or null
* @see VarSymbol#setLazyConstValue
*/
public Object attribLazyConstantValue(Env env,
JCVariableDecl variable,
Type type) {
DiagnosticPosition prevLintPos
= deferredLintHandler.setPos(variable.pos());
final JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile);
try {
Type itype = attribExpr(variable.init, env, type);
if (variable.isImplicitlyTyped()) {
//fixup local variable type
type = variable.type = variable.sym.type = chk.checkLocalVarType(variable, itype.baseType(), variable.name);
}
if (itype.constValue() != null) {
return coerce(itype, type).constValue();
} else {
return null;
}
} finally {
log.useSource(prevSource);
deferredLintHandler.setPos(prevLintPos);
}
}
/** 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) {
final DiagnosticPosition pos = tree.hasTag(TYPEAPPLY) ?
(((JCTypeApply) tree).clazz).pos() : tree.pos();
if (t.tsym.isAnonymous()) {
log.error(pos, Errors.CantInheritFromAnon);
return types.createErrorType(t);
}
if (t.isErroneous())
return t;
if (t.hasTag(TYPEVAR) && !classExpected && !interfaceExpected) {
// check that type variable is already visible
if (t.getUpperBound() == null) {
log.error(pos, Errors.IllegalForwardRef);
return types.createErrorType(t);
}
} else {
t = chk.checkClassType(pos, t, checkExtensible);
}
if (interfaceExpected && (t.tsym.flags() & INTERFACE) == 0) {
log.error(pos, Errors.IntfExpectedHere);
// 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(pos, Errors.NoIntfExpectedHere);
return types.createErrorType(t);
}
if (checkExtensible &&
((t.tsym.flags() & FINAL) != 0)) {
log.error(pos,
Errors.CantInheritFromFinal(t.tsym));
}
chk.checkNonCyclic(pos, t);
return t;
}
Type attribIdentAsEnumType(Env env, JCIdent id) {
Assert.check((env.enclClass.sym.flags() & ENUM) != 0);
id.type = env.info.scope.owner.enclClass().type;
id.sym = env.info.scope.owner.enclClass();
return id.type;
}
public void visitClassDef(JCClassDecl tree) {
Optional localCacheContext =
Optional.ofNullable(env.info.attributionMode.isSpeculative ?
argumentAttr.withLocalCacheContext() : null);
try {
// Local and anonymous classes have not been entered yet, so we need to
// do it now.
if (env.info.scope.owner.kind.matches(KindSelector.VAL_MTH)) {
enter.classEnter(tree, env);
} else {
// If this class declaration is part of a class level annotation,
// as in @MyAnno(new Object() {}) class MyClass {}, enter it in
// order to simplify later steps and allow for sensible error
// messages.
if (env.tree.hasTag(NEWCLASS) && TreeInfo.isInAnnotation(env, tree))
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
// 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)) {
c.flags_field |= NOOUTERTHIS;
}
attribClass(tree.pos(), c);
result = tree.type = c.type;
}
} finally {
localCacheContext.ifPresent(LocalCacheContext::leave);
}
}
public void visitMethodDef(JCMethodDecl tree) {
MethodSymbol m = tree.sym;
boolean isDefaultMethod = (m.flags() & DEFAULT) != 0;
Lint lint = env.info.lint.augment(m);
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(env, tree, m);
if (isDefaultMethod && types.overridesObjectMethod(m.enclClass(), m)) {
log.error(tree, Errors.DefaultOverridesObjectMember(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() ||
tree.recvparam != null))
log.error(tree.params.nonEmpty() ?
tree.params.head.pos() :
tree.recvparam.pos(),
Errors.IntfAnnotationMembersCantHaveParams);
// 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.
if (tree.restype != null && !tree.restype.type.hasTag(VOID))
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);
}
if (env.enclClass.sym.isRecord() && tree.sym.owner.kind == TYP) {
// lets find if this method is an accessor
Optional extends RecordComponent> recordComponent = env.enclClass.sym.getRecordComponents().stream()
.filter(rc -> rc.accessor == tree.sym && (rc.accessor.flags_field & GENERATED_MEMBER) == 0).findFirst();
if (recordComponent.isPresent()) {
// the method is a user defined accessor lets check that everything is fine
if (!tree.sym.isPublic()) {
log.error(tree, Errors.InvalidAccessorMethodInRecord(env.enclClass.sym, Fragments.MethodMustBePublic));
}
if (!types.isSameType(tree.sym.type.getReturnType(), recordComponent.get().type)) {
log.error(tree, Errors.InvalidAccessorMethodInRecord(env.enclClass.sym,
Fragments.AccessorReturnTypeDoesntMatch(tree.sym, recordComponent.get())));
}
if (tree.sym.type.asMethodType().thrown != null && !tree.sym.type.asMethodType().thrown.isEmpty()) {
log.error(tree,
Errors.InvalidAccessorMethodInRecord(env.enclClass.sym, Fragments.AccessorMethodCantThrowException));
}
if (!tree.typarams.isEmpty()) {
log.error(tree,
Errors.InvalidAccessorMethodInRecord(env.enclClass.sym, Fragments.AccessorMethodMustNotBeGeneric));
}
if (tree.sym.isStatic()) {
log.error(tree,
Errors.InvalidAccessorMethodInRecord(env.enclClass.sym, Fragments.AccessorMethodMustNotBeStatic));
}
}
if (tree.name == names.init) {
// if this a constructor other than the canonical one
if ((tree.sym.flags_field & RECORD) == 0) {
JCMethodInvocation app = TreeInfo.firstConstructorCall(tree);
if (app == null ||
TreeInfo.name(app.meth) != names._this ||
!checkFirstConstructorStat(app, tree, false)) {
log.error(tree, Errors.FirstStatementMustBeCallToAnotherConstructor(env.enclClass.sym));
}
} else {
// but if it is the canonical:
/* if user generated, then it shouldn't:
* - have an accessibility stricter than that of the record type
* - explicitly invoke any other constructor
*/
if ((tree.sym.flags_field & GENERATEDCONSTR) == 0) {
if (Check.protection(m.flags()) > Check.protection(env.enclClass.sym.flags())) {
log.error(tree,
(env.enclClass.sym.flags() & AccessFlags) == 0 ?
Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical,
env.enclClass.sym.name,
Fragments.CanonicalMustNotHaveStrongerAccess("package")
) :
Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical,
env.enclClass.sym.name,
Fragments.CanonicalMustNotHaveStrongerAccess(asFlagSet(env.enclClass.sym.flags() & AccessFlags))
)
);
}
JCMethodInvocation app = TreeInfo.firstConstructorCall(tree);
if (app != null &&
(TreeInfo.name(app.meth) == names._this ||
TreeInfo.name(app.meth) == names._super) &&
checkFirstConstructorStat(app, tree, false)) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, env.enclClass.sym.name,
Fragments.CanonicalMustNotContainExplicitConstructorInvocation));
}
}
// also we want to check that no type variables have been defined
if (!tree.typarams.isEmpty()) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, env.enclClass.sym.name, Fragments.CanonicalMustNotDeclareTypeVariables));
}
/* and now we need to check that the constructor's arguments are exactly the same as those of the
* record components
*/
List extends RecordComponent> recordComponents = env.enclClass.sym.getRecordComponents();
List recordFieldTypes = TreeInfo.recordFields(env.enclClass).map(vd -> vd.sym.type);
for (JCVariableDecl param: tree.params) {
boolean paramIsVarArgs = (param.sym.flags_field & VARARGS) != 0;
if (!types.isSameType(param.type, recordFieldTypes.head) ||
(recordComponents.head.isVarargs() != paramIsVarArgs)) {
log.error(param, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, env.enclClass.sym.name,
Fragments.TypeMustBeIdenticalToCorrespondingRecordComponentType));
}
recordComponents = recordComponents.tail;
recordFieldTypes = recordFieldTypes.tail;
}
}
}
}
// annotation method checks
if ((owner.flags() & ANNOTATION) != 0) {
// annotation method cannot have throws clause
if (tree.thrown.nonEmpty()) {
log.error(tree.thrown.head.pos(),
Errors.ThrowsNotAllowedInIntfAnnotation);
}
// annotation method cannot declare type-parameters
if (tree.typarams.nonEmpty()) {
log.error(tree.typarams.head.pos(),
Errors.IntfAnnotationMembersCantHaveTypeParams);
}
// 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);
}
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 (tree.defaultValue != null) {
if ((owner.flags() & ANNOTATION) == 0)
log.error(tree.pos(),
Errors.DefaultAllowedInIntfAnnotationMember);
}
if (isDefaultMethod || (tree.sym.flags() & (ABSTRACT | NATIVE)) == 0)
log.error(tree.pos(), Errors.MissingMethBodyOrDeclAbstract);
} else if ((tree.sym.flags() & (ABSTRACT|DEFAULT|PRIVATE)) == ABSTRACT) {
if ((owner.flags() & INTERFACE) != 0) {
log.error(tree.body.pos(), Errors.IntfMethCantHaveBody);
} else {
log.error(tree.pos(), Errors.AbstractMethCantHaveBody);
}
} else if ((tree.mods.flags & NATIVE) != 0) {
log.error(tree.pos(), Errors.NativeMethCantHaveBody);
} 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.getConstructorInvocationName(body.stats, names) == names.empty) {
JCStatement supCall = make.at(body.pos).Exec(make.Apply(List.nil(),
make.Ident(names._super), make.Idents(List.nil())));
body.stats = body.stats.prepend(supCall);
} 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(),
Errors.CallToSuperNotAllowedInEnumCtor(env.enclClass.sym));
}
if (env.enclClass.sym.isRecord() && (tree.sym.flags_field & RECORD) != 0) { // we are seeing the canonical constructor
List recordComponentNames = TreeInfo.recordFields(env.enclClass).map(vd -> vd.sym.name);
List initParamNames = tree.sym.params.map(p -> p.name);
if (!initParamNames.equals(recordComponentNames)) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, env.enclClass.sym.name, Fragments.CanonicalWithNameMismatch));
}
if (tree.sym.type.asMethodType().thrown != null && !tree.sym.type.asMethodType().thrown.isEmpty()) {
log.error(tree,
Errors.InvalidCanonicalConstructorInRecord(
TreeInfo.isCompactConstructor(tree) ? Fragments.Compact : Fragments.Canonical,
env.enclClass.sym.name,
Fragments.ThrowsClauseNotAllowedForCanonicalConstructor(
TreeInfo.isCompactConstructor(tree) ? Fragments.Compact : Fragments.Canonical)));
}
}
}
// Attribute all type annotations in the body
annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null);
annotate.flush();
// Attribute method body.
attribStat(tree.body, localEnv);
}
localEnv.info.scope.leave();
result = tree.type = m.type;
} 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 || env.info.scope.owner.kind == VAR) {
if (tree.sym != null) {
// parameters have already been entered
env.info.scope.enter(tree.sym);
} else {
if (tree.isImplicitlyTyped() && (tree.getModifiers().flags & PARAMETER) == 0) {
if (tree.init == null) {
//cannot use 'var' without initializer
log.error(tree, Errors.CantInferLocalVarType(tree.name, Fragments.LocalMissingInit));
tree.vartype = make.Erroneous();
} else {
Fragment msg = canInferLocalVarType(tree);
if (msg != null) {
//cannot use 'var' with initializer which require an explicit target
//(e.g. lambda, method reference, array initializer).
log.error(tree, Errors.CantInferLocalVarType(tree.name, msg));
tree.vartype = make.Erroneous();
}
}
}
try {
annotate.blockAnnotations();
memberEnter.memberEnter(tree, env);
} finally {
annotate.unblockAnnotations();
}
}
} else {
if (tree.init != null) {
// Field initializer expression need to be entered.
annotate.queueScanTreeAndTypeAnnotate(tree.init, env, tree.sym, tree.pos());
annotate.flush();
}
}
VarSymbol v = tree.sym;
Lint lint = env.info.lint.augment(v);
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 && !tree.isImplicitlyTyped());
try {
v.getConstValue(); // ensure compile-time constant initializer is evaluated
deferredLintHandler.flush(tree.pos());
chk.checkDeprecatedAnnotation(tree.pos(), v);
if (tree.init != null) {
if ((v.flags_field & FINAL) == 0 ||
!memberEnter.needsLazyConstValue(tree.init)) {
// Not a compile-time constant
// 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);
if (tree.isImplicitlyTyped()) {
//fixup local variable type
v.type = chk.checkLocalVarType(tree, tree.init.type.baseType(), tree.name);
}
}
if (tree.isImplicitlyTyped()) {
setSyntheticVariableType(tree, v.type);
}
}
result = tree.type = v.type;
if (env.enclClass.sym.isRecord() && tree.sym.owner.kind == TYP && !v.isStatic()) {
if (isNonArgsMethodInObject(v.name)) {
log.error(tree, Errors.IllegalRecordComponentName(v));
}
}
}
finally {
chk.setLint(prevLint);
}
}
private boolean isNonArgsMethodInObject(Name name) {
for (Symbol s : syms.objectType.tsym.members().getSymbolsByName(name, s -> s.kind == MTH)) {
if (s.type.getParameterTypes().isEmpty()) {
return true;
}
}
return false;
}
Fragment canInferLocalVarType(JCVariableDecl tree) {
LocalInitScanner lis = new LocalInitScanner();
lis.scan(tree.init);
return lis.badInferenceMsg;
}
static class LocalInitScanner extends TreeScanner {
Fragment badInferenceMsg = null;
boolean needsTarget = true;
@Override
public void visitNewArray(JCNewArray tree) {
if (tree.elemtype == null && needsTarget) {
badInferenceMsg = Fragments.LocalArrayMissingTarget;
}
}
@Override
public void visitLambda(JCLambda tree) {
if (needsTarget) {
badInferenceMsg = Fragments.LocalLambdaMissingTarget;
}
}
@Override
public void visitTypeCast(JCTypeCast tree) {
boolean prevNeedsTarget = needsTarget;
try {
needsTarget = false;
super.visitTypeCast(tree);
} finally {
needsTarget = prevNeedsTarget;
}
}
@Override
public void visitReference(JCMemberReference tree) {
if (needsTarget) {
badInferenceMsg = Fragments.LocalMrefMissingTarget;
}
}
@Override
public void visitNewClass(JCNewClass tree) {
boolean prevNeedsTarget = needsTarget;
try {
needsTarget = false;
super.visitNewClass(tree);
} finally {
needsTarget = prevNeedsTarget;
}
}
@Override
public void visitApply(JCMethodInvocation tree) {
boolean prevNeedsTarget = needsTarget;
try {
needsTarget = false;
super.visitApply(tree);
} finally {
needsTarget = prevNeedsTarget;
}
}
}
public void visitSkip(JCSkip tree) {
result = null;
}
public void visitBlock(JCBlock tree) {
if (env.info.scope.owner.kind == TYP || env.info.scope.owner.kind == ERR) {
// Block is a static or instance initializer;
// let the owner of the environment be a freshly
// created BLOCK-method.
Symbol fakeOwner =
new MethodSymbol(tree.flags | BLOCK |
env.info.scope.owner.flags() & STRICTFP, names.empty, null,
env.info.scope.owner);
final Env localEnv =
env.dup(tree, env.info.dup(env.info.scope.dupUnshared(fakeOwner)));
if ((tree.flags & STATIC) != 0) localEnv.info.staticLevel++;
// Attribute all type annotations in the block
annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, localEnv.info.scope.owner, null);
annotate.flush();
attribStats(tree.stats, localEnv);
{
// 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.appendClassInitTypeAttributes(tas);
} else {
cs.appendInitTypeAttributes(tas);
}
}
} 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);
if (!breaksOutOf(tree, tree.body)) {
//include condition's body when false after the while, if cannot get out of the loop
MatchBindings condBindings = matchBindings;
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
}
result = null;
}
public void visitWhileLoop(JCWhileLoop tree) {
attribExpr(tree.cond, env, syms.booleanType);
MatchBindings condBindings = matchBindings;
// include condition's bindings when true in the body:
Env whileEnv = bindingEnv(env, condBindings.bindingsWhenTrue);
try {
attribStat(tree.body, whileEnv.dup(tree));
} finally {
whileEnv.info.scope.leave();
}
if (!breaksOutOf(tree, tree.body)) {
//include condition's bindings when false after the while, if cannot get out of the loop
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
}
result = null;
}
private boolean breaksOutOf(JCTree loop, JCTree body) {
preFlow(body);
return flow.breaksOutOf(env, loop, body, make);
}
public void visitForLoop(JCForLoop tree) {
Env loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
MatchBindings condBindings = MatchBindingsComputer.EMPTY;
try {
attribStats(tree.init, loopEnv);
if (tree.cond != null) {
attribExpr(tree.cond, loopEnv, syms.booleanType);
// include condition's bindings when true in the body and step:
condBindings = matchBindings;
}
Env bodyEnv = bindingEnv(loopEnv, condBindings.bindingsWhenTrue);
try {
bodyEnv.tree = tree; // before, we were not in loop!
attribStats(tree.step, bodyEnv);
attribStat(tree.body, bodyEnv);
} finally {
bodyEnv.info.scope.leave();
}
result = null;
}
finally {
loopEnv.info.scope.leave();
}
if (!breaksOutOf(tree, tree.body)) {
//include condition's body when false after the while, if cannot get out of the loop
condBindings.bindingsWhenFalse.forEach(env.info.scope::enter);
condBindings.bindingsWhenFalse.forEach(BindingSymbol::preserveBinding);
}
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
Env loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
try {
//the Formal Parameter of a for-each loop is not in the scope when
//attributing the for-each expression; we mimic this by attributing
//the for-each expression first (against original scope).
Type exprType = types.cvarUpperBound(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(),
Errors.ForeachNotApplicableToType(exprType,
Fragments.TypeReqArrayOrIterable));
elemtype = types.createErrorType(exprType);
} else {
List iterableParams = base.allparams();
elemtype = iterableParams.isEmpty()
? syms.objectType
: types.wildUpperBound(iterableParams.head);
}
}
if (tree.var.isImplicitlyTyped()) {
Type inferredType = chk.checkLocalVarType(tree.var, elemtype, tree.var.name);
setSyntheticVariableType(tree.var, inferredType);
}
attribStat(tree.var, loopEnv);
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(),
Errors.LabelAlreadyInUse(tree.label));
break;
}
env1 = env1.next;
}
attribStat(tree.body, env.dup(tree));
result = null;
}
public void visitSwitch(JCSwitch tree) {
handleSwitch(tree, tree.selector, tree.cases, (c, caseEnv) -> {
attribStats(c.stats, caseEnv);
});
result = null;
}
public void visitSwitchExpression(JCSwitchExpression tree) {
tree.polyKind = (pt().hasTag(NONE) && pt() != Type.recoveryType && pt() != Infer.anyPoly) ?
PolyKind.STANDALONE : PolyKind.POLY;
if (tree.polyKind == PolyKind.POLY && resultInfo.pt.hasTag(VOID)) {
//this means we are returning a poly conditional from void-compatible lambda expression
resultInfo.checkContext.report(tree, diags.fragment(Fragments.SwitchExpressionTargetCantBeVoid));
result = tree.type = types.createErrorType(resultInfo.pt);
return;
}
ResultInfo condInfo = tree.polyKind == PolyKind.STANDALONE ?
unknownExprInfo :
resultInfo.dup(switchExpressionContext(resultInfo.checkContext));
ListBuffer caseTypePositions = new ListBuffer<>();
ListBuffer caseTypes = new ListBuffer<>();
handleSwitch(tree, tree.selector, tree.cases, (c, caseEnv) -> {
caseEnv.info.yieldResult = condInfo;
attribStats(c.stats, caseEnv);
new TreeScanner() {
@Override
public void visitYield(JCYield brk) {
if (brk.target == tree) {
caseTypePositions.append(brk.value != null ? brk.value.pos() : brk.pos());
caseTypes.append(brk.value != null ? brk.value.type : syms.errType);
}
super.visitYield(brk);
}
@Override public void visitClassDef(JCClassDecl tree) {}
@Override public void visitLambda(JCLambda tree) {}
}.scan(c.stats);
});
if (tree.cases.isEmpty()) {
log.error(tree.pos(),
Errors.SwitchExpressionEmpty);
} else if (caseTypes.isEmpty()) {
log.error(tree.pos(),
Errors.SwitchExpressionNoResultExpressions);
}
Type owntype = (tree.polyKind == PolyKind.STANDALONE) ? condType(caseTypePositions.toList(), caseTypes.toList()) : pt();
result = tree.type = check(tree, owntype, KindSelector.VAL, resultInfo);
}
//where:
CheckContext switchExpressionContext(CheckContext checkContext) {
return new Check.NestedCheckContext(checkContext) {
//this will use enclosing check context to check compatibility of
//subexpression against target type; if we are in a method check context,
//depending on whether boxing is allowed, we could have incompatibilities
@Override
public void report(DiagnosticPosition pos, JCDiagnostic details) {
enclosingContext.report(pos, diags.fragment(Fragments.IncompatibleTypeInSwitchExpression(details)));
}
};
}
private void handleSwitch(JCTree switchTree,
JCExpression selector,
List cases,
BiConsumer> attribCase) {
Type seltype = attribExpr(selector, env);
Env switchEnv =
env.dup(switchTree, env.info.dup(env.info.scope.dup()));
try {
boolean enumSwitch = (seltype.tsym.flags() & Flags.ENUM) != 0;
boolean stringSwitch = types.isSameType(seltype, syms.stringType);
if (!enumSwitch && !stringSwitch)
seltype = chk.checkType(selector.pos(), seltype, syms.intType);
// Attribute all cases and
// check that there are no duplicate case labels or default clauses.
Set