mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-14 00:49:42 +00:00
8268850: AST model for 'var' variables should more closely adhere to the source code
Reviewed-by: vromero
This commit is contained in:
parent
fd48f68a2c
commit
a39a1f10f7
@ -266,6 +266,13 @@ public interface Tree {
|
||||
*/
|
||||
PRIMITIVE_TYPE(PrimitiveTypeTree.class),
|
||||
|
||||
/**
|
||||
* Used for instances of {@link VarTypeTree}.
|
||||
*
|
||||
* @since 27
|
||||
*/
|
||||
VAR_TYPE(VarTypeTree.class),
|
||||
|
||||
/**
|
||||
* Used for instances of {@link ReturnTree}.
|
||||
*/
|
||||
|
||||
@ -498,6 +498,15 @@ public interface TreeVisitor<R,P> {
|
||||
*/
|
||||
R visitPrimitiveType(PrimitiveTypeTree node, P p);
|
||||
|
||||
/**
|
||||
* Visits a {@code VarTypeTree} node.
|
||||
* @param node the node being visited
|
||||
* @param p a parameter value
|
||||
* @return a result value
|
||||
* @since 27
|
||||
*/
|
||||
R visitVarType(VarTypeTree node, P p);
|
||||
|
||||
/**
|
||||
* Visits a {@code TypeParameterTree} node.
|
||||
* @param node the node being visited
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2026, 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.source.tree;
|
||||
|
||||
import javax.lang.model.type.TypeKind;
|
||||
|
||||
/**
|
||||
* A tree node for a {@code var} contextual keyword used as a type.
|
||||
*
|
||||
* @since 27
|
||||
*/
|
||||
public interface VarTypeTree extends Tree {
|
||||
}
|
||||
@ -65,10 +65,13 @@ public interface VariableTree extends StatementTree {
|
||||
*/
|
||||
ExpressionTree getNameExpression();
|
||||
|
||||
/**
|
||||
* Returns the type of the variable being declared.
|
||||
* @return the type
|
||||
*/
|
||||
/// {@return the type of the variable being declared.}
|
||||
///
|
||||
/// @apiNote
|
||||
/// The type of the variable can be one of the following:
|
||||
/// - if the variable is declared using {@code var}, then the returned value is a {@link VarTypeTree},
|
||||
/// - if the variable is a lambda parameter declared without a type (i.e. relying on type inferrence), then the returned value is {@code null},
|
||||
/// - otherwise, the variable is declared with an explicit type, and the returned value is that type.
|
||||
Tree getType();
|
||||
|
||||
/**
|
||||
|
||||
@ -803,6 +803,21 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
|
||||
return defaultAction(node, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @implSpec This implementation calls {@code defaultAction}.
|
||||
*
|
||||
* @param node {@inheritDoc}
|
||||
* @param p {@inheritDoc}
|
||||
* @return the result of {@code defaultAction}
|
||||
* @since 27
|
||||
*/
|
||||
@Override
|
||||
public R visitVarType(VarTypeTree node, P p) {
|
||||
return defaultAction(node, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
||||
@ -939,6 +939,21 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @implSpec This implementation returns {@code null}.
|
||||
*
|
||||
* @param node {@inheritDoc}
|
||||
* @param p {@inheritDoc}
|
||||
* @return the result of scanning
|
||||
* @since 27
|
||||
*/
|
||||
@Override
|
||||
public R visitVarType(VarTypeTree node, P p) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
||||
@ -370,7 +370,6 @@ public class Analyzer {
|
||||
*/
|
||||
JCVariableDecl rewriteVarType(JCVariableDecl oldTree) {
|
||||
JCVariableDecl newTree = copier.copy(oldTree);
|
||||
newTree.vartype = null;
|
||||
return newTree;
|
||||
}
|
||||
|
||||
@ -751,7 +750,6 @@ public class Analyzer {
|
||||
if (oldLambda.paramKind == ParameterKind.IMPLICIT) {
|
||||
//reset implicit lambda parameters (whose type might have been set during attr)
|
||||
newLambda.paramKind = ParameterKind.IMPLICIT;
|
||||
newLambda.params.forEach(p -> p.vartype = null);
|
||||
}
|
||||
return newLambda;
|
||||
}
|
||||
|
||||
@ -1263,18 +1263,18 @@ public class Attr extends JCTree.Visitor {
|
||||
// parameters have already been entered
|
||||
env.info.scope.enter(tree.sym);
|
||||
} else {
|
||||
if (tree.isImplicitlyTyped() && (tree.getModifiers().flags & PARAMETER) == 0) {
|
||||
if (tree.isImplicitlyTyped() && (tree.getModifiers().flags & PARAMETER) == 0 && tree.type == null) {
|
||||
if (tree.init == null) {
|
||||
//cannot use 'var' without initializer
|
||||
log.error(tree, Errors.CantInferLocalVarType(tree.name, Fragments.LocalMissingInit));
|
||||
tree.vartype = make.at(tree.pos()).Erroneous();
|
||||
tree.type = syms.errType;
|
||||
} 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.at(tree.pos()).Erroneous();
|
||||
tree.type = syms.errType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1323,7 +1323,7 @@ public class Attr extends JCTree.Visitor {
|
||||
}
|
||||
}
|
||||
if (tree.isImplicitlyTyped()) {
|
||||
setSyntheticVariableType(tree, v.type);
|
||||
setupImplicitlyTypedVariable(tree, v.type);
|
||||
}
|
||||
}
|
||||
result = tree.type = v.type;
|
||||
@ -1597,7 +1597,8 @@ public class Attr extends JCTree.Visitor {
|
||||
}
|
||||
if (tree.var.isImplicitlyTyped()) {
|
||||
Type inferredType = chk.checkLocalVarType(tree.var, elemtype, tree.var.name);
|
||||
setSyntheticVariableType(tree.var, inferredType);
|
||||
tree.var.type = inferredType;
|
||||
setupImplicitlyTypedVariable(tree.var, inferredType);
|
||||
}
|
||||
attribStat(tree.var, loopEnv);
|
||||
chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type);
|
||||
@ -3223,8 +3224,10 @@ public class Attr extends JCTree.Visitor {
|
||||
Type argType = arityMismatch ?
|
||||
syms.errType :
|
||||
actuals.head;
|
||||
if (params.head.isImplicitlyTyped()) {
|
||||
setSyntheticVariableType(params.head, argType);
|
||||
if (params.head.type == null &&
|
||||
params.head.isImplicitlyTyped()) { //error recovery
|
||||
params.head.type = argType;
|
||||
setupImplicitlyTypedVariable(params.head, argType);
|
||||
}
|
||||
params.head.sym = null;
|
||||
actuals = actuals.isEmpty() ?
|
||||
@ -3442,7 +3445,7 @@ public class Attr extends JCTree.Visitor {
|
||||
JCLambda lambda = (JCLambda)tree;
|
||||
List<Type> argtypes = List.nil();
|
||||
for (JCVariableDecl param : lambda.params) {
|
||||
argtypes = param.vartype != null && param.vartype.type != null ?
|
||||
argtypes = !param.isImplicitlyTyped() && param.vartype.type != null ?
|
||||
argtypes.append(param.vartype.type) :
|
||||
argtypes.append(syms.errType);
|
||||
}
|
||||
@ -4218,7 +4221,7 @@ public class Attr extends JCTree.Visitor {
|
||||
|
||||
public void visitBindingPattern(JCBindingPattern tree) {
|
||||
Type type;
|
||||
if (tree.var.vartype != null) {
|
||||
if (!tree.var.isImplicitlyTyped()) {
|
||||
type = attribType(tree.var.vartype, env);
|
||||
} else {
|
||||
type = resultInfo.pt;
|
||||
@ -4231,11 +4234,11 @@ public class Attr extends JCTree.Visitor {
|
||||
if (chk.checkUnique(tree.var.pos(), v, env.info.scope)) {
|
||||
chk.checkTransparentVar(tree.var.pos(), v, env.info.scope);
|
||||
}
|
||||
chk.validate(tree.var.vartype, env, true);
|
||||
if (tree.var.isImplicitlyTyped()) {
|
||||
setSyntheticVariableType(tree.var, type == Type.noType ? syms.errType
|
||||
: type);
|
||||
setupImplicitlyTypedVariable(tree.var, type == Type.noType ? syms.errType
|
||||
: type);
|
||||
}
|
||||
chk.validate(tree.var.vartype, env, true);
|
||||
annotate.annotateLater(tree.var.mods.annotations, env, v);
|
||||
if (!tree.var.isImplicitlyTyped()) {
|
||||
annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v);
|
||||
@ -4254,7 +4257,7 @@ public class Attr extends JCTree.Visitor {
|
||||
public void visitRecordPattern(JCRecordPattern tree) {
|
||||
Type site;
|
||||
|
||||
if (tree.deconstructor == null) {
|
||||
if (tree.deconstructor.hasTag(VARTYPE)) {
|
||||
log.error(tree.pos(), Errors.DeconstructionPatternVarNotAllowed);
|
||||
tree.record = syms.errSymbol;
|
||||
site = tree.type = types.createErrorType(tree.record.type);
|
||||
@ -4270,6 +4273,7 @@ public class Attr extends JCTree.Visitor {
|
||||
}
|
||||
tree.type = tree.deconstructor.type = type;
|
||||
site = types.capture(tree.type);
|
||||
chk.validate(tree.deconstructor, env, true);
|
||||
}
|
||||
|
||||
List<Type> expectedRecordTypes;
|
||||
@ -4315,7 +4319,6 @@ public class Attr extends JCTree.Visitor {
|
||||
} finally {
|
||||
localEnv.info.scope.leave();
|
||||
}
|
||||
chk.validate(tree.deconstructor, env, true);
|
||||
result = tree.type;
|
||||
matchBindings = new MatchBindings(outBindings.toList(), List.nil());
|
||||
}
|
||||
@ -5731,15 +5734,20 @@ public class Attr extends JCTree.Visitor {
|
||||
return types.capture(type);
|
||||
}
|
||||
|
||||
private void setSyntheticVariableType(JCVariableDecl tree, Type type) {
|
||||
if (type.isErroneous()) {
|
||||
tree.vartype = make.at(tree.pos()).Erroneous();
|
||||
} else if (tree.declaredUsingVar()) {
|
||||
Assert.check(tree.typePos != Position.NOPOS);
|
||||
tree.vartype = make.at(tree.typePos).Type(type);
|
||||
} else {
|
||||
tree.vartype = make.at(tree.pos()).Type(type);
|
||||
private void setupImplicitlyTypedVariable(JCVariableDecl tree, Type type) {
|
||||
Assert.check(tree.isImplicitlyTyped());
|
||||
|
||||
type.complete();
|
||||
|
||||
if (tree.vartype == null) {
|
||||
return ;
|
||||
}
|
||||
|
||||
Assert.check(tree.vartype.hasTag(VARTYPE));
|
||||
|
||||
JCVarType vartype = (JCVarType) tree.vartype;
|
||||
|
||||
vartype.type = type;
|
||||
}
|
||||
|
||||
public void validateTypeAnnotations(JCTree tree, boolean sigOnly) {
|
||||
@ -6068,9 +6076,6 @@ public class Attr extends JCTree.Visitor {
|
||||
that.sym = new VarSymbol(0, that.name, that.type, syms.noSymbol);
|
||||
that.sym.adr = 0;
|
||||
}
|
||||
if (that.vartype == null) {
|
||||
that.vartype = make.at(Position.NOPOS).Erroneous();
|
||||
}
|
||||
super.visitVarDef(that);
|
||||
}
|
||||
|
||||
@ -6145,6 +6150,11 @@ public class Attr extends JCTree.Visitor {
|
||||
syms.noSymbol);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarType(JCVarType that) {
|
||||
initTypeIfNeeded(that);
|
||||
}
|
||||
}
|
||||
// </editor-fold>
|
||||
|
||||
|
||||
@ -117,11 +117,6 @@ public class AttrRecover {
|
||||
? formals.head : ((ArrayType) formals.head).elemtype;
|
||||
if (arg.hasTag(JCTree.Tag.LAMBDA)) {
|
||||
final JCTree.JCLambda lambda = (JCLambda) arg;
|
||||
if (lambda.paramKind == JCLambda.ParameterKind.IMPLICIT) {
|
||||
for (JCVariableDecl var : lambda.params) {
|
||||
var.vartype = null; //reset type
|
||||
}
|
||||
}
|
||||
if (types.isFunctionalInterface(formal)) {
|
||||
Type functionalType = types.findDescriptorType(formal);
|
||||
boolean voidCompatible = functionalType.getReturnType().hasTag(TypeTag.VOID);
|
||||
|
||||
@ -3692,11 +3692,12 @@ public class Lower extends TreeTranslator {
|
||||
if (tree.var.type.isPrimitive())
|
||||
vardefinit = make.TypeCast(types.cvarUpperBound(iteratorTarget), vardefinit);
|
||||
else
|
||||
vardefinit = make.TypeCast(tree.var.type, vardefinit);
|
||||
vardefinit = transTypes.coerce(attrEnv, vardefinit, tree.var.type);
|
||||
JCVariableDecl indexDef = (JCVariableDecl)make.VarDef(tree.var.mods,
|
||||
tree.var.name,
|
||||
tree.var.vartype,
|
||||
vardefinit).setType(tree.var.type);
|
||||
vardefinit,
|
||||
tree.var.declKind).setType(tree.var.type);
|
||||
indexDef.sym = tree.var.sym;
|
||||
JCBlock body = make.Block(0, List.of(indexDef, tree.body));
|
||||
body.bracePos = TreeInfo.endPos(tree.body);
|
||||
|
||||
@ -114,7 +114,7 @@ public class MemberEnter extends JCTree.Visitor {
|
||||
ListBuffer<Type> argbuf = new ListBuffer<>();
|
||||
for (List<JCVariableDecl> l = params; l.nonEmpty(); l = l.tail) {
|
||||
memberEnter(l.head, env);
|
||||
argbuf.append(l.head.vartype.type);
|
||||
argbuf.append(l.head.sym.type);
|
||||
}
|
||||
|
||||
// Attribute result type, if one is given.
|
||||
@ -276,9 +276,13 @@ public class MemberEnter extends JCTree.Visitor {
|
||||
tree.vartype.type = atype.makeVarargs();
|
||||
}
|
||||
WriteableScope enclScope = enter.enterScope(env);
|
||||
Type vartype = tree.isImplicitlyTyped()
|
||||
? env.info.scope.owner.kind == MTH ? Type.noType : syms.errType
|
||||
: tree.vartype.type;
|
||||
Type vartype = switch (tree.declKind) {
|
||||
case IMPLICIT -> tree.type;
|
||||
case EXPLICIT -> tree.vartype.type;
|
||||
case VAR -> tree.type != null ? tree.type
|
||||
: env.info.scope.owner.kind == MTH ? Type.noType
|
||||
: syms.errType;
|
||||
};
|
||||
Name name = tree.name;
|
||||
VarSymbol v = new VarSymbol(0, name, vartype, enclScope.owner);
|
||||
v.flags_field = chk.checkFlags(tree.mods.flags | tree.declKind.additionalSymbolFlags, v, tree);
|
||||
|
||||
@ -93,6 +93,7 @@ import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
|
||||
import com.sun.tools.javac.tree.JCTree.JCTypeUnion;
|
||||
import com.sun.tools.javac.tree.JCTree.JCUnary;
|
||||
import com.sun.tools.javac.tree.JCTree.JCUses;
|
||||
import com.sun.tools.javac.tree.JCTree.JCVarType;
|
||||
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
|
||||
import com.sun.tools.javac.tree.JCTree.JCWildcard;
|
||||
@ -631,6 +632,11 @@ public class TreeDiffer extends TreeScanner {
|
||||
result = tree.typetag == that.typetag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarType(JCVarType tree) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeIntersection(JCTypeIntersection tree) {
|
||||
JCTypeIntersection that = (JCTypeIntersection) parameter;
|
||||
|
||||
@ -977,13 +977,11 @@ public class JavacParser implements Parser {
|
||||
pattern = toP(F.at(token.pos).AnyPattern());
|
||||
}
|
||||
else {
|
||||
int varTypePos = Position.NOPOS;
|
||||
if (parsedType == null) {
|
||||
boolean var = token.kind == IDENTIFIER && token.name() == names.var;
|
||||
e = unannotatedType(allowVar, TYPE | NOLAMBDA);
|
||||
if (var) {
|
||||
varTypePos = e.pos;
|
||||
e = null;
|
||||
e = replaceTypeWithVar(e);
|
||||
}
|
||||
} else {
|
||||
e = parsedType;
|
||||
@ -1021,12 +1019,9 @@ public class JavacParser implements Parser {
|
||||
name = names.empty;
|
||||
}
|
||||
JCVariableDecl var = toP(F.at(varPos).VarDef(mods, name, e, null,
|
||||
varTypePos != Position.NOPOS ? JCVariableDecl.DeclKind.VAR : JCVariableDecl.DeclKind.EXPLICIT,
|
||||
varTypePos));
|
||||
if (e == null) {
|
||||
if (var.name == names.underscore && !allowVar) {
|
||||
log.error(DiagnosticFlag.SYNTAX, varPos, Errors.UseOfUnderscoreNotAllowed);
|
||||
}
|
||||
e.hasTag(VARTYPE) ? JCVariableDecl.DeclKind.VAR : JCVariableDecl.DeclKind.EXPLICIT));
|
||||
if (var.name == names.underscore && !allowVar) {
|
||||
log.error(DiagnosticFlag.SYNTAX, varPos, Errors.UseOfUnderscoreNotAllowed);
|
||||
}
|
||||
pattern = toP(F.at(pos).BindingPattern(var));
|
||||
}
|
||||
@ -2178,8 +2173,7 @@ public class JavacParser implements Parser {
|
||||
&& restrictedTypeName(param.vartype, true) != null) {
|
||||
checkSourceLevel(param.pos, Feature.VAR_SYNTAX_IMPLICIT_LAMBDAS);
|
||||
param.declKind = JCVariableDecl.DeclKind.VAR;
|
||||
param.typePos = TreeInfo.getStartPos(param.vartype);
|
||||
param.vartype = null;
|
||||
param.vartype = replaceTypeWithVar(param.vartype);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3792,7 +3786,6 @@ public class JavacParser implements Parser {
|
||||
*/
|
||||
JCVariableDecl variableDeclaratorRest(int pos, JCModifiers mods, JCExpression type, Name name,
|
||||
boolean reqInit, Comment dc, boolean localDecl, boolean compound) {
|
||||
boolean declaredUsingVar = false;
|
||||
JCExpression init = null;
|
||||
type = bracketsOpt(type);
|
||||
|
||||
@ -3818,7 +3811,6 @@ public class JavacParser implements Parser {
|
||||
syntaxError(token.pos, Errors.Expected(EQ));
|
||||
}
|
||||
|
||||
int varTypePos = Position.NOPOS;
|
||||
JCTree elemType = TreeInfo.innermostType(type, true);
|
||||
if (elemType.hasTag(IDENT)) {
|
||||
Name typeName = ((JCIdent) elemType).name;
|
||||
@ -3829,21 +3821,27 @@ public class JavacParser implements Parser {
|
||||
//error - 'var' and arrays
|
||||
reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedArray(typeName));
|
||||
} else {
|
||||
declaredUsingVar = true;
|
||||
varTypePos = elemType.pos;
|
||||
type = replaceTypeWithVar(type);
|
||||
|
||||
if (compound)
|
||||
//error - 'var' in compound local var decl
|
||||
reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedCompound(typeName));
|
||||
//implicit type
|
||||
type = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
JCVariableDecl result = toP(F.at(pos).VarDef(mods, name, type, init,
|
||||
declaredUsingVar ? JCVariableDecl.DeclKind.VAR : JCVariableDecl.DeclKind.EXPLICIT, varTypePos));
|
||||
type.hasTag(VARTYPE) ? JCVariableDecl.DeclKind.VAR : JCVariableDecl.DeclKind.EXPLICIT));
|
||||
return attach(result, dc);
|
||||
}
|
||||
|
||||
JCExpression replaceTypeWithVar(JCExpression type) {
|
||||
JCVarType varType = F.at(type).VarType();
|
||||
|
||||
varType.endpos = type.endpos;
|
||||
|
||||
return varType;
|
||||
}
|
||||
|
||||
Name restrictedTypeName(JCExpression e, boolean shouldWarn) {
|
||||
switch (e.getTag()) {
|
||||
case IDENT:
|
||||
@ -3954,11 +3952,10 @@ public class JavacParser implements Parser {
|
||||
name = names.empty;
|
||||
}
|
||||
|
||||
boolean declaredUsingVar = type != null && type.hasTag(IDENT) && ((JCIdent)type).name == names.var;
|
||||
boolean declaredUsingVar = type != null && type.hasTag(VARTYPE);
|
||||
JCVariableDecl.DeclKind declKind = declaredUsingVar ? JCVariableDecl.DeclKind.VAR :
|
||||
type != null ? JCVariableDecl.DeclKind.EXPLICIT : JCVariableDecl.DeclKind.IMPLICIT;
|
||||
int typePos = type != null ? type.pos : pos;
|
||||
return toP(F.at(pos).VarDef(mods, name, type, null, declKind, typePos));
|
||||
return toP(F.at(pos).VarDef(mods, name, type, null, declKind));
|
||||
}
|
||||
|
||||
/** Resources = Resource { ";" Resources }
|
||||
|
||||
@ -277,6 +277,10 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
*/
|
||||
TYPEIDENT,
|
||||
|
||||
/** 'var' type.
|
||||
*/
|
||||
VARTYPE,
|
||||
|
||||
/** Array types, of type TypeArray.
|
||||
*/
|
||||
TYPEARRAY,
|
||||
@ -1038,15 +1042,13 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
public VarSymbol sym;
|
||||
/** how the variable's type was declared */
|
||||
public DeclKind declKind;
|
||||
/** a source code position to use for "vartype" when null (can happen if declKind != EXPLICIT) */
|
||||
public int typePos;
|
||||
|
||||
protected JCVariableDecl(JCModifiers mods,
|
||||
Name name,
|
||||
JCExpression vartype,
|
||||
JCExpression init,
|
||||
VarSymbol sym) {
|
||||
this(mods, name, vartype, init, sym, DeclKind.EXPLICIT, Position.NOPOS);
|
||||
this(mods, name, vartype, init, sym, DeclKind.EXPLICIT);
|
||||
}
|
||||
|
||||
protected JCVariableDecl(JCModifiers mods,
|
||||
@ -1054,21 +1056,19 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
JCExpression vartype,
|
||||
JCExpression init,
|
||||
VarSymbol sym,
|
||||
DeclKind declKind,
|
||||
int typePos) {
|
||||
DeclKind declKind) {
|
||||
this.mods = mods;
|
||||
this.name = name;
|
||||
this.vartype = vartype;
|
||||
this.init = init;
|
||||
this.sym = sym;
|
||||
this.declKind = declKind;
|
||||
this.typePos = typePos;
|
||||
}
|
||||
|
||||
protected JCVariableDecl(JCModifiers mods,
|
||||
JCExpression nameexpr,
|
||||
JCExpression vartype) {
|
||||
this(mods, null, vartype, null, null, DeclKind.EXPLICIT, Position.NOPOS);
|
||||
this(mods, null, vartype, null, null, DeclKind.EXPLICIT);
|
||||
this.nameexpr = nameexpr;
|
||||
if (nameexpr.hasTag(Tag.IDENT)) {
|
||||
this.name = ((JCIdent)nameexpr).name;
|
||||
@ -1078,8 +1078,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
}
|
||||
}
|
||||
|
||||
@DefinedBy(Api.COMPILER_TREE)
|
||||
public boolean isImplicitlyTyped() {
|
||||
return vartype == null;
|
||||
return declKind != DeclKind.EXPLICIT;
|
||||
}
|
||||
|
||||
public boolean declaredUsingVar() {
|
||||
@ -2047,7 +2048,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
this.params = params;
|
||||
this.body = body;
|
||||
if (params.isEmpty() ||
|
||||
params.head.vartype != null) {
|
||||
!params.head.isImplicitlyTyped()) {
|
||||
paramKind = ParameterKind.EXPLICIT;
|
||||
} else {
|
||||
paramKind = ParameterKind.IMPLICIT;
|
||||
@ -2827,6 +2828,24 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
}
|
||||
}
|
||||
|
||||
public static class JCVarType extends JCExpression implements VarTypeTree {
|
||||
public JCVarType() {}
|
||||
@Override
|
||||
public void accept(Visitor v) { v.visitVarType(this); }
|
||||
|
||||
@DefinedBy(Api.COMPILER_TREE)
|
||||
public Kind getKind() { return Kind.VAR_TYPE; }
|
||||
|
||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||
public <R,D> R accept(TreeVisitor<R,D> v, D d) {
|
||||
return v.visitVarType(this, d);
|
||||
}
|
||||
@Override
|
||||
public Tag getTag() {
|
||||
return VARTYPE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An array type, A[]
|
||||
*/
|
||||
@ -3604,6 +3623,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
public void visitIdent(JCIdent that) { visitTree(that); }
|
||||
public void visitLiteral(JCLiteral that) { visitTree(that); }
|
||||
public void visitTypeIdent(JCPrimitiveTypeTree that) { visitTree(that); }
|
||||
public void visitVarType(JCVarType that) { visitTree(that); }
|
||||
public void visitTypeArray(JCArrayTypeTree that) { visitTree(that); }
|
||||
public void visitTypeApply(JCTypeApply that) { visitTree(that); }
|
||||
public void visitTypeUnion(JCTypeUnion that) { visitTree(that); }
|
||||
|
||||
@ -1529,6 +1529,15 @@ public class Pretty extends JCTree.Visitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarType(JCVarType that) {
|
||||
try {
|
||||
print("var");
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitTypeArray(JCArrayTypeTree tree) {
|
||||
try {
|
||||
printBaseElementType(tree);
|
||||
|
||||
@ -480,6 +480,12 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
|
||||
return M.at(t.pos).TypeIdent(t.typetag);
|
||||
}
|
||||
|
||||
@DefinedBy(Api.COMPILER_TREE)
|
||||
public JCTree visitVarType(VarTypeTree node, P p) {
|
||||
JCVarType t = (JCVarType) node;
|
||||
return M.at(t.pos).VarType();
|
||||
}
|
||||
|
||||
@DefinedBy(Api.COMPILER_TREE)
|
||||
public JCTree visitTypeParameter(TypeParameterTree node, P p) {
|
||||
JCTypeParameter t = (JCTypeParameter) node;
|
||||
@ -551,7 +557,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
|
||||
JCExpression vartype = copy(t.vartype, p);
|
||||
if (t.nameexpr == null) {
|
||||
JCExpression init = copy(t.init, p);
|
||||
return M.at(t.pos).VarDef(mods, t.name, vartype, init, t.declKind, t.typePos);
|
||||
return M.at(t.pos).VarDef(mods, t.name, vartype, init, t.declKind);
|
||||
} else {
|
||||
JCExpression nameexpr = copy(t.nameexpr, p);
|
||||
return M.at(t.pos).ReceiverVarDef(mods, nameexpr, vartype);
|
||||
|
||||
@ -621,8 +621,6 @@ public class TreeInfo {
|
||||
return node.mods.pos;
|
||||
} else if (node.vartype != null) {
|
||||
return getStartPos(node.vartype);
|
||||
} else if (node.typePos != Position.NOPOS) {
|
||||
return node.typePos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -238,8 +238,8 @@ public class TreeMaker implements JCTree.Factory {
|
||||
}
|
||||
|
||||
public JCVariableDecl VarDef(JCModifiers mods, Name name, JCExpression vartype, JCExpression init,
|
||||
JCVariableDecl.DeclKind declKind, int typePos) {
|
||||
JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null, declKind, typePos);
|
||||
JCVariableDecl.DeclKind declKind) {
|
||||
JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null, declKind);
|
||||
tree.pos = pos;
|
||||
return tree;
|
||||
}
|
||||
@ -566,6 +566,12 @@ public class TreeMaker implements JCTree.Factory {
|
||||
return tree;
|
||||
}
|
||||
|
||||
public JCVarType VarType() {
|
||||
JCVarType tree = new JCVarType();
|
||||
tree.pos = pos;
|
||||
return tree;
|
||||
}
|
||||
|
||||
public JCArrayTypeTree TypeArray(JCExpression elemtype) {
|
||||
JCArrayTypeTree tree = new JCArrayTypeTree(elemtype);
|
||||
tree.pos = pos;
|
||||
|
||||
@ -361,6 +361,9 @@ public class TreeScanner extends Visitor {
|
||||
public void visitTypeIdent(JCPrimitiveTypeTree tree) {
|
||||
}
|
||||
|
||||
public void visitVarType(JCVarType tree) {
|
||||
}
|
||||
|
||||
public void visitTypeArray(JCArrayTypeTree tree) {
|
||||
scan(tree.elemtype);
|
||||
}
|
||||
|
||||
@ -418,6 +418,10 @@ public class TreeTranslator extends JCTree.Visitor {
|
||||
result = tree;
|
||||
}
|
||||
|
||||
public void visitVarType(JCVarType tree) {
|
||||
result = tree;
|
||||
}
|
||||
|
||||
public void visitTypeArray(JCArrayTypeTree tree) {
|
||||
tree.elemtype = translate(tree.elemtype);
|
||||
result = tree;
|
||||
|
||||
@ -337,7 +337,7 @@ class Eval {
|
||||
Set<String> anonymousClasses = Collections.emptySet();
|
||||
StringBuilder sbBrackets = new StringBuilder();
|
||||
Tree baseType = vt.getType();
|
||||
if (baseType != null) {
|
||||
if (vt.getType() != null && vt.getType().getKind() != Tree.Kind.VAR_TYPE) {
|
||||
tds.scan(baseType); // Not dependent on initializer
|
||||
fullTypeName = displayType = typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
|
||||
while (baseType instanceof ArrayTypeTree) {
|
||||
|
||||
@ -816,7 +816,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||
SourcePositions sp = trees.getSourcePositions();
|
||||
List<Token> tokens = new ArrayList<>();
|
||||
Context ctx = new Context();
|
||||
ctx.put(DiagnosticListener.class, (DiagnosticListener) d -> {});
|
||||
ctx.put(DiagnosticListener.class, (DiagnosticListener<?>) d -> {});
|
||||
Scanner scanner = ScannerFactory.instance(ctx).newScanner(wrappedCode, false);
|
||||
Log.instance(ctx).useSource(cut.getSourceFile());
|
||||
scanner.nextToken();
|
||||
@ -932,7 +932,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||
@Override
|
||||
public Void visitVariable(VariableTree node, Void p) {
|
||||
int pos = ((JCTree) node).pos;
|
||||
if (sp.getEndPosition(cut, node.getType()) == (-1)) {
|
||||
if (node.getType() != null && node.getType().getKind() == Kind.VAR_TYPE) {
|
||||
Token varCandidate = findTokensBefore(pos, TokenKind.IDENTIFIER);
|
||||
if (varCandidate != null && "var".equals(varCandidate.name().toString())) {
|
||||
addKeyword.accept(varCandidate);
|
||||
|
||||
@ -4,6 +4,4 @@ VarVariables.java:24:14: compiler.err.annotation.type.not.applicable
|
||||
VarVariables.java:27:14: compiler.err.annotation.type.not.applicable
|
||||
VarVariables.java:32:14: compiler.err.annotation.type.not.applicable
|
||||
VarVariables.java:36:37: compiler.err.annotation.type.not.applicable
|
||||
VarVariables.java:32:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.AutoCloseable
|
||||
VarVariables.java:36:41: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.String
|
||||
8 errors
|
||||
6 errors
|
||||
|
||||
@ -4,6 +4,4 @@ VarVariables.java:24:14: compiler.err.annotation.type.not.applicable
|
||||
VarVariables.java:27:14: compiler.err.annotation.type.not.applicable
|
||||
VarVariables.java:32:14: compiler.err.annotation.type.not.applicable
|
||||
VarVariables.java:36:37: compiler.err.annotation.type.not.applicable
|
||||
VarVariables.java:32:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.AutoCloseable
|
||||
VarVariables.java:36:41: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.String
|
||||
8 errors
|
||||
6 errors
|
||||
|
||||
393
test/langtools/tools/javac/lvti/VarAccessibility.java
Normal file
393
test/langtools/tools/javac/lvti/VarAccessibility.java
Normal file
@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright (c) 2026, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Check behavior of var when the inferred type is inaccessible
|
||||
* @library /tools/lib
|
||||
* @modules java.logging
|
||||
* java.sql
|
||||
* jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.compiler/com.sun.tools.javac.util
|
||||
* @build toolbox.ToolBox toolbox.JavacTask
|
||||
* @run junit VarAccessibility
|
||||
*/
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import toolbox.JavacTask;
|
||||
import toolbox.JavaTask;
|
||||
import toolbox.Task;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
public class VarAccessibility {
|
||||
|
||||
private static final ToolBox tb = new ToolBox();
|
||||
|
||||
@Test
|
||||
public void testInaccessibleInferredTypeLocalVariable() throws Exception {
|
||||
Path base = Paths.get(".");
|
||||
Path src = base.resolve("src");
|
||||
Path classes = base.resolve("classes");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
package p1;
|
||||
public class API {
|
||||
public static PackagePrivate get() {
|
||||
return new PackagePrivate();
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
class PackagePrivate {
|
||||
public String toString() {
|
||||
return "pass";
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p2;
|
||||
import p1.API;
|
||||
public class Test {
|
||||
public static void main(String... args) {
|
||||
var v = API.get();
|
||||
System.out.println(v);
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
new JavacTask(tb)
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll();
|
||||
|
||||
var out = new JavaTask(tb)
|
||||
.classpath(classes.toString())
|
||||
.className("p2.Test")
|
||||
.run()
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.STDOUT);
|
||||
|
||||
var expectedOut = List.of("pass");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInaccessibleInferredTypeForEachIterable() throws Exception {
|
||||
Path base = Paths.get(".");
|
||||
Path src = base.resolve("src");
|
||||
Path classes = base.resolve("classes");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
package p1;
|
||||
import java.util.List;
|
||||
public class API {
|
||||
public static Iterable<PackagePrivate> get() {
|
||||
return List.of(new PackagePrivate());
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
class PackagePrivate {
|
||||
public String toString() {
|
||||
return "pass";
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p2;
|
||||
import p1.API;
|
||||
public class Test {
|
||||
public static void main(String... args) {
|
||||
for (var v : API.get()) {
|
||||
System.out.println(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
var out = new JavacTask(tb)
|
||||
.options("-XDrawDiagnostics")
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.FAIL)
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.DIRECT);
|
||||
|
||||
var expectedOut = List.of(
|
||||
"Test.java:5:22: compiler.err.not.def.public.cant.access: p1.PackagePrivate, p1",
|
||||
"1 error");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInaccessibleInferredTypeForEachArray() throws Exception {
|
||||
Path base = Paths.get(".");
|
||||
Path src = base.resolve("src");
|
||||
Path classes = base.resolve("classes");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
package p1;
|
||||
import java.util.List;
|
||||
public class API {
|
||||
public static PackagePrivate[] get() {
|
||||
return new PackagePrivate[] {new PackagePrivate()};
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
class PackagePrivate {
|
||||
public String toString() {
|
||||
return "pass";
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p2;
|
||||
import p1.API;
|
||||
public class Test {
|
||||
public static void main(String... args) {
|
||||
for (var v : API.get()) {
|
||||
System.out.println(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
new JavacTask(tb)
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll();
|
||||
|
||||
var out = new JavaTask(tb)
|
||||
.classpath(classes.toString())
|
||||
.className("p2.Test")
|
||||
.run()
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.STDOUT);
|
||||
|
||||
var expectedOut = List.of("pass");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInaccessibleInferredTypeLambda() throws Exception {
|
||||
Path base = Paths.get(".");
|
||||
Path src = base.resolve("src");
|
||||
Path classes = base.resolve("classes");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
package p1;
|
||||
import java.util.function.Consumer;
|
||||
public class API {
|
||||
public static void run(Consumer<PackagePrivate> c) {
|
||||
c.accept(new PackagePrivate());
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
class PackagePrivate {
|
||||
public String toString() {
|
||||
return "pass";
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p2;
|
||||
import p1.API;
|
||||
public class Test {
|
||||
public static void main(String... args) {
|
||||
API.run(v -> System.out.println(v));
|
||||
API.run((var v) -> System.out.println(v));
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
var out = new JavacTask(tb)
|
||||
.options("-XDrawDiagnostics")
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.FAIL)
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.DIRECT);
|
||||
|
||||
var expectedOut = List.of(
|
||||
"Test.java:5:17: compiler.err.not.def.public.cant.access: p1.PackagePrivate, p1",
|
||||
"Test.java:6:17: compiler.err.not.def.public.cant.access: p1.PackagePrivate, p1",
|
||||
"2 errors");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInaccessibleInferredTypeCanUse() throws Exception {
|
||||
Path base = Paths.get(".");
|
||||
Path src = base.resolve("src");
|
||||
Path classes = base.resolve("classes");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
package p1;
|
||||
public class API {
|
||||
public static PackagePrivate get() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
class PackagePrivate {
|
||||
public String toString() {
|
||||
return "pass";
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p2;
|
||||
import p1.API;
|
||||
public class Test {
|
||||
public static void main(String... args) {
|
||||
var v = API.get();
|
||||
System.out.println(v.toString());
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
List<String> log = new JavacTask(tb)
|
||||
.options("-XDrawDiagnostics")
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.FAIL)
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.DIRECT);
|
||||
|
||||
var expectedOut = List.of(
|
||||
"Test.java:6:29: compiler.err.not.def.access.class.intf.cant.access: toString(), p1.PackagePrivate",
|
||||
"1 error"
|
||||
);
|
||||
|
||||
if (!Objects.equals(expectedOut, log)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + log);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInaccessibleInferredBindingPattern() throws Exception {
|
||||
Path base = Paths.get(".");
|
||||
Path src = base.resolve("src");
|
||||
Path classes = base.resolve("classes");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
package p1;
|
||||
public record API(PackagePrivate p) {
|
||||
public static API create() {
|
||||
return new API(new PackagePrivate());
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
class PackagePrivate {
|
||||
public String toString() {
|
||||
return "pass";
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p2;
|
||||
import p1.API;
|
||||
public class Test {
|
||||
public static void main(String... args) {
|
||||
Object o = API.create();
|
||||
if (o instanceof API(var v)) {
|
||||
System.out.println(v.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
List<String> log = new JavacTask(tb)
|
||||
.options("-XDrawDiagnostics")
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.FAIL)
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.DIRECT);
|
||||
|
||||
var expectedOut = List.of(
|
||||
"Test.java:7:33: compiler.err.not.def.access.class.intf.cant.access: toString(), p1.PackagePrivate",
|
||||
"1 error"
|
||||
);
|
||||
|
||||
if (!Objects.equals(expectedOut, log)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + log);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
193
test/langtools/tools/javac/lvti/VarWarnings.java
Normal file
193
test/langtools/tools/javac/lvti/VarWarnings.java
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2026, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Check behavior of warnings related to var
|
||||
* @library /tools/lib
|
||||
* @modules java.logging
|
||||
* java.sql
|
||||
* jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.compiler/com.sun.tools.javac.util
|
||||
* @build toolbox.ToolBox toolbox.JavacTask
|
||||
* @run junit VarWarnings
|
||||
*/
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import toolbox.JavacTask;
|
||||
import toolbox.Task;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
public class VarWarnings {
|
||||
|
||||
private static final ToolBox tb = new ToolBox();
|
||||
|
||||
@Test
|
||||
public void testDeprecationWarning() throws Exception {
|
||||
Path base = Paths.get(".");
|
||||
Path src = base.resolve("src");
|
||||
Path classes = base.resolve("classes");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
package p1;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@SuppressWarnings("deprecation")
|
||||
public class API {
|
||||
public static DeprOutter<DeprInner> get() {
|
||||
return new DeprOutter<>();
|
||||
}
|
||||
public static Iterable<DeprOutter<DeprInner>> getIterable() {
|
||||
return null;
|
||||
}
|
||||
public static void run(Consumer<DeprOutter<DeprInner>> task) {
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
@Deprecated
|
||||
public class DeprInner {
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
@Deprecated
|
||||
public class DeprOutter<T> {
|
||||
public T get() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p2;
|
||||
import p1.API;
|
||||
public class Test {
|
||||
public static void main(String... args) {
|
||||
var v1 = API.get();
|
||||
API.run(v -> v.get().toString());
|
||||
API.run((var v) -> v.get().toString());
|
||||
for (var v2 : API.getIterable()) {}
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
var out = new JavacTask(tb)
|
||||
.options("-XDrawDiagnostics",
|
||||
"-Werror",
|
||||
"-Xlint:deprecation")
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.DIRECT);
|
||||
|
||||
var expectedOut = List.of("");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRawTypeWarning() throws Exception {
|
||||
Path base = Paths.get(".");
|
||||
Path src = base.resolve("src");
|
||||
Path classes = base.resolve("classes");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
package p1;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class API {
|
||||
public static RawOutter<RawInner> get() {
|
||||
return new RawOutter<>();
|
||||
}
|
||||
public static Iterable<RawOutter<RawInner>> getIterable() {
|
||||
return null;
|
||||
}
|
||||
public static void run(Consumer<RawOutter<RawInner>> task) {
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
public class RawInner<T> {
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p1;
|
||||
public class RawOutter<T> {
|
||||
public T get() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package p2;
|
||||
import p1.API;
|
||||
public class Test {
|
||||
public static void main(String... args) {
|
||||
var v1 = API.get();
|
||||
API.run(v -> v.get().toString());
|
||||
API.run((var v) -> v.get().toString());
|
||||
for (var v2 : API.getIterable()) {}
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
var out = new JavacTask(tb)
|
||||
.options("-XDrawDiagnostics",
|
||||
"-Werror",
|
||||
"-Xlint:rawtypes")
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.DIRECT);
|
||||
|
||||
var expectedOut = List.of("");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -86,10 +86,12 @@ public class DeclarationEndPositions {
|
||||
|
||||
// For variable declarations using "var", verify the "var" position
|
||||
if (tree instanceof JCVariableDecl varDecl && varDecl.declaredUsingVar()) {
|
||||
int vpos = varDecl.typePos;
|
||||
if (!input.substring(vpos).startsWith("var")) {
|
||||
int varStart = varDecl.vartype.getStartPosition();
|
||||
int varEnd = varDecl.vartype.getEndPosition();
|
||||
|
||||
if (!input.substring(varStart, varEnd).startsWith("var")) {
|
||||
throw new AssertionError(String.format(
|
||||
"wrong %s pos %d for \"%s\" in \"%s\"", "var", vpos, tree, input));
|
||||
"wrong %s start pos %d end pos %d for \"%s\" in \"%s\"", "var", varStart, varEnd, tree, input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +87,9 @@ import javax.tools.ToolProvider;
|
||||
|
||||
import com.sun.source.tree.CaseTree;
|
||||
import com.sun.source.tree.DefaultCaseLabelTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.ModuleTree;
|
||||
import com.sun.source.tree.VarTypeTree;
|
||||
import com.sun.source.util.TreePathScanner;
|
||||
import com.sun.tools.javac.api.JavacTaskPool;
|
||||
import com.sun.tools.javac.api.JavacTaskPool.Worker;
|
||||
@ -1070,8 +1072,8 @@ public class JavacParserTest extends TestCase {
|
||||
VariableTree stmt2 = (VariableTree) method.getBody().getStatements().get(1);
|
||||
Tree v1Type = stmt1.getType();
|
||||
Tree v2Type = stmt2.getType();
|
||||
assertEquals("Implicit type for v1 is not correct: ", Kind.PRIMITIVE_TYPE, v1Type.getKind());
|
||||
assertEquals("Implicit type for v2 is not correct: ", Kind.PRIMITIVE_TYPE, v2Type.getKind());
|
||||
assertEquals("Implicit type for v1 is not correct: ", Kind.VAR_TYPE, v1Type.getKind());
|
||||
assertEquals("Implicit type for v2 is not correct: ", Kind.VAR_TYPE, v2Type.getKind());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -3151,6 +3153,37 @@ public class JavacParserTest extends TestCase {
|
||||
codes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVarPositions() throws IOException {
|
||||
String code = """
|
||||
public class Test {
|
||||
void t() {
|
||||
var v =
|
||||
}
|
||||
}
|
||||
""";
|
||||
DiagnosticCollector<JavaFileObject> coll =
|
||||
new DiagnosticCollector<>();
|
||||
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll,
|
||||
null,
|
||||
null, Arrays.asList(new MyFileObject(code)));
|
||||
Trees trees = Trees.instance(ct);
|
||||
SourcePositions sp = trees.getSourcePositions();
|
||||
CompilationUnitTree cut = ct.parse().iterator().next();
|
||||
new TreeScanner<Void, Void>() {
|
||||
@Override
|
||||
public Void visitVarType(VarTypeTree node, Void p) {
|
||||
long start = sp.getStartPosition(cut, node);
|
||||
long end = sp.getEndPosition(cut, node);
|
||||
String text = code.substring((int) start, (int) end);
|
||||
assertEquals("var start pos",
|
||||
"var",
|
||||
text);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void run(String[] args) throws Exception {
|
||||
int passed = 0, failed = 0;
|
||||
final Pattern p = (args != null && args.length > 0)
|
||||
|
||||
@ -83,7 +83,7 @@ public class BindingPatternVarTypeModel {
|
||||
new TreeScanner<Void, Void>() {
|
||||
@Override
|
||||
public Void visitBindingPattern(BindingPatternTree node, Void p) {
|
||||
if (node.getVariable().getType().getKind() != Tree.Kind.PRIMITIVE_TYPE) {
|
||||
if (node.getVariable().getType().getKind() != Tree.Kind.VAR_TYPE) {
|
||||
throw new AssertionError("Unexpected type for var: " +
|
||||
node.getVariable().getType().getKind() +
|
||||
":" + node.getVariable().getType());
|
||||
|
||||
@ -79,7 +79,7 @@ public class InstanceOfModelTest {
|
||||
List<String> expectedInstanceOf = List.of(
|
||||
"null:R",
|
||||
"R r:R",
|
||||
"R(int v):null"
|
||||
"R(var v):null"
|
||||
);
|
||||
|
||||
if (!Objects.equals(expectedInstanceOf, instanceOf)) {
|
||||
|
||||
@ -52,27 +52,27 @@ public class VarTree {
|
||||
public static void main(String... args) throws Exception {
|
||||
VarTree test = new VarTree();
|
||||
test.run("|var testVar = 0;| ",
|
||||
"int testVar = 0");
|
||||
"var testVar = 0");
|
||||
test.run("|var testVar = 0;| undef undef;",
|
||||
"int testVar = 0");
|
||||
"var testVar = 0");
|
||||
test.run("|final var testVar = 0;| ",
|
||||
"final int testVar = 0");
|
||||
"final var testVar = 0");
|
||||
test.run("for (|var testVar| : java.util.Arrays.asList(0, 1)) {}",
|
||||
"java.lang.Integer testVar");
|
||||
"var testVar");
|
||||
test.run("for (|final var testVar| : java.util.Arrays.asList(0, 1)) {}",
|
||||
"final java.lang.Integer testVar");
|
||||
"final var testVar");
|
||||
test.run("java.util.function.Consumer<String> c = |testVar| -> {};",
|
||||
"java.lang.String testVar");
|
||||
"/*missing*/ testVar"); //TODO: is the /*missing*/ here ideal?
|
||||
test.run("java.util.function.Consumer<String> c = (|testVar|) -> {};",
|
||||
"java.lang.String testVar");
|
||||
"/*missing*/ testVar"); //TODO: is the /*missing*/ here ideal?
|
||||
test.run("java.util.function.Consumer<String> c = (|var testVar|) -> {};",
|
||||
"java.lang.String testVar");
|
||||
"var testVar");
|
||||
test.run("java.util.function.Consumer<String> c = (|final var testVar|) -> {};",
|
||||
"final java.lang.String testVar");
|
||||
"final var testVar");
|
||||
test.run("record Rec(int x) { }; switch (null) { case Rec(|var testVar|) -> {} default -> {} };",
|
||||
"int testVar");
|
||||
"var testVar");
|
||||
test.run("record Rec(int x) { }; switch (null) { case Rec(|final var testVar|) -> {} default -> {} };",
|
||||
"final int testVar");
|
||||
"final var testVar");
|
||||
}
|
||||
|
||||
void run(String code, String expected) throws IOException {
|
||||
@ -136,11 +136,13 @@ public class VarTree {
|
||||
throw new AssertionError("Unexpected span: " + snip);
|
||||
}
|
||||
|
||||
int typeStart = (int) trees.getSourcePositions().getStartPosition(cut, node.getType());
|
||||
int typeEnd = (int) trees.getSourcePositions().getEndPosition(cut, node.getType());
|
||||
if (node.getType() != null) {
|
||||
int typeStart = (int) trees.getSourcePositions().getStartPosition(cut, node.getType());
|
||||
int typeEnd = (int) trees.getSourcePositions().getEndPosition(cut, node.getType());
|
||||
|
||||
if (typeStart != (-1) && typeEnd != (-1)) {
|
||||
throw new AssertionError("Unexpected type position: " + typeStart + ", " + typeEnd);
|
||||
if (typeStart + 3 != typeEnd) {
|
||||
throw new AssertionError("Unexpected type position: " + typeStart + ", " + typeEnd);
|
||||
}
|
||||
}
|
||||
|
||||
found[0] = true;
|
||||
|
||||
@ -25,6 +25,9 @@ public class VarWarnPosition {
|
||||
|
||||
// Test 4
|
||||
Consumer<Depr> c3 = (final var d) -> { };
|
||||
|
||||
// Test 5
|
||||
var d = deprecatedList.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
VarWarnPosition.java:18:14: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package
|
||||
VarWarnPosition.java:21:18: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package
|
||||
VarWarnPosition.java:21:28: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package
|
||||
VarWarnPosition.java:24:18: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package
|
||||
VarWarnPosition.java:24:30: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package
|
||||
VarWarnPosition.java:27:18: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package
|
||||
VarWarnPosition.java:27:36: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package
|
||||
7 warnings
|
||||
3 warnings
|
||||
Loading…
x
Reference in New Issue
Block a user