diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java index 845203c8735..152883b4930 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java @@ -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}. */ diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java index a2a33ccf8eb..f2a067971d1 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java @@ -498,6 +498,15 @@ public interface TreeVisitor { */ 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 diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/VarTypeTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/VarTypeTree.java new file mode 100644 index 00000000000..bfe13ebdce1 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/VarTypeTree.java @@ -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 { +} diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/VariableTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/VariableTree.java index a1191b2f8e6..11dc41577be 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/VariableTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/VariableTree.java @@ -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(); /** diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java index 77305d56b1e..917df861b70 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java @@ -803,6 +803,21 @@ public class SimpleTreeVisitor implements TreeVisitor { 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} * diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java index 29e81c206e0..ca8b785d8cb 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java @@ -939,6 +939,21 @@ public class TreeScanner implements TreeVisitor { 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} * diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java index f45e8500000..fb5576a82e1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java @@ -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; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 48e0238fb07..83b684e1225 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -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 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 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); + } } // diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrRecover.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrRecover.java index 716345fa077..092035c84dd 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrRecover.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrRecover.java @@ -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); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 69161fd682c..be603ffc9ca 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -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); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java index e02fb9849c9..cb0e6d4676b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java @@ -114,7 +114,7 @@ public class MemberEnter extends JCTree.Visitor { ListBuffer argbuf = new ListBuffer<>(); for (List 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); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java index bbc12f1fe80..4890b749a90 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java @@ -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; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 40f91a004fd..16f26c836f8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -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 } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index c54507bed3f..f0a8b6034df 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -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 accept(TreeVisitor 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); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java index d953663a6d7..c22dfd61637 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java @@ -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); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java index 9efc6a9d895..95e2976f14e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java @@ -480,6 +480,12 @@ public class TreeCopier

implements TreeVisitor { 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

implements TreeVisitor { 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); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index aa616f3f580..ffb259c6a8b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -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; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java index 0e71df99bdc..77391493ad2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -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; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java index b9ae35da9df..0d1216217c8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java @@ -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); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java index 63778fb42ff..5b06e76bf33 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java @@ -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; diff --git a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java index bc6f6d30236..70f3b70fd66 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java @@ -337,7 +337,7 @@ class Eval { Set 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) { diff --git a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java index 1cf0c85702f..35faab231af 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java @@ -816,7 +816,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis { SourcePositions sp = trees.getSourcePositions(); List 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); diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out index 5b16d28d053..2b447e4a842 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out @@ -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 diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out index 2586e7883d6..7f055040855 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out @@ -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 diff --git a/test/langtools/tools/javac/lvti/VarAccessibility.java b/test/langtools/tools/javac/lvti/VarAccessibility.java new file mode 100644 index 00000000000..d39d706faea --- /dev/null +++ b/test/langtools/tools/javac/lvti/VarAccessibility.java @@ -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 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 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 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 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); + + } + } +} diff --git a/test/langtools/tools/javac/lvti/VarWarnings.java b/test/langtools/tools/javac/lvti/VarWarnings.java new file mode 100644 index 00000000000..a1fbadc0393 --- /dev/null +++ b/test/langtools/tools/javac/lvti/VarWarnings.java @@ -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 get() { + return new DeprOutter<>(); + } + public static Iterable> getIterable() { + return null; + } + public static void run(Consumer> task) { + } + } + """, + """ + package p1; + @Deprecated + public class DeprInner { + } + """, + """ + package p1; + @Deprecated + public class DeprOutter { + 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 get() { + return new RawOutter<>(); + } + public static Iterable> getIterable() { + return null; + } + public static void run(Consumer> task) { + } + } + """, + """ + package p1; + public class RawInner { + } + """, + """ + package p1; + public class RawOutter { + 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); + + } + } + +} diff --git a/test/langtools/tools/javac/parser/DeclarationEndPositions.java b/test/langtools/tools/javac/parser/DeclarationEndPositions.java index c61a92e80cd..fe4ca8c1652 100644 --- a/test/langtools/tools/javac/parser/DeclarationEndPositions.java +++ b/test/langtools/tools/javac/parser/DeclarationEndPositions.java @@ -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)); } } } diff --git a/test/langtools/tools/javac/parser/JavacParserTest.java b/test/langtools/tools/javac/parser/JavacParserTest.java index 25aeb19f1bb..0bce1ef017c 100644 --- a/test/langtools/tools/javac/parser/JavacParserTest.java +++ b/test/langtools/tools/javac/parser/JavacParserTest.java @@ -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 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() { + @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) diff --git a/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java b/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java index e63cbeb2008..01d742eb9e3 100644 --- a/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java +++ b/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java @@ -83,7 +83,7 @@ public class BindingPatternVarTypeModel { new TreeScanner() { @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()); diff --git a/test/langtools/tools/javac/patterns/InstanceOfModelTest.java b/test/langtools/tools/javac/patterns/InstanceOfModelTest.java index b756c9263bd..288abec9659 100644 --- a/test/langtools/tools/javac/patterns/InstanceOfModelTest.java +++ b/test/langtools/tools/javac/patterns/InstanceOfModelTest.java @@ -79,7 +79,7 @@ public class InstanceOfModelTest { List expectedInstanceOf = List.of( "null:R", "R r:R", - "R(int v):null" + "R(var v):null" ); if (!Objects.equals(expectedInstanceOf, instanceOf)) { diff --git a/test/langtools/tools/javac/tree/VarTree.java b/test/langtools/tools/javac/tree/VarTree.java index 6efd080bd4f..70caf66862e 100644 --- a/test/langtools/tools/javac/tree/VarTree.java +++ b/test/langtools/tools/javac/tree/VarTree.java @@ -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 c = |testVar| -> {};", - "java.lang.String testVar"); + "/*missing*/ testVar"); //TODO: is the /*missing*/ here ideal? test.run("java.util.function.Consumer c = (|testVar|) -> {};", - "java.lang.String testVar"); + "/*missing*/ testVar"); //TODO: is the /*missing*/ here ideal? test.run("java.util.function.Consumer c = (|var testVar|) -> {};", - "java.lang.String testVar"); + "var testVar"); test.run("java.util.function.Consumer 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; diff --git a/test/langtools/tools/javac/tree/VarWarnPosition.java b/test/langtools/tools/javac/tree/VarWarnPosition.java index f1d5f4346d0..5cdfa41d6ef 100644 --- a/test/langtools/tools/javac/tree/VarWarnPosition.java +++ b/test/langtools/tools/javac/tree/VarWarnPosition.java @@ -25,6 +25,9 @@ public class VarWarnPosition { // Test 4 Consumer c3 = (final var d) -> { }; + + // Test 5 + var d = deprecatedList.get(0); } } diff --git a/test/langtools/tools/javac/tree/VarWarnPosition.out b/test/langtools/tools/javac/tree/VarWarnPosition.out index 87a4ca3a1fd..0bd92acd035 100644 --- a/test/langtools/tools/javac/tree/VarWarnPosition.out +++ b/test/langtools/tools/javac/tree/VarWarnPosition.out @@ -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 \ No newline at end of file