8302344: Compiler Implementation for Unnamed patterns and variables (Preview)

8307444: java.lang.AssertionError when using unnamed patterns
8307482: Compiler should accept var _ in nested patterns in switch case
8307007: Implementation for javax.lang.model for unnamed variables (Preview)
8308312: Compiler should fail when a local variable declaration does not include an Identifier and does not have an initializer
8308309: Compiler should accept mixed masked and unmasked variables in lambda parameters

Co-authored-by: Jan Lahoda <jlahoda@openjdk.org>
Co-authored-by: Aggelos Biboudis <abimpoudis@openjdk.org>
Reviewed-by: vromero, darcy
This commit is contained in:
Aggelos Biboudis 2023-05-22 08:52:36 +00:00 committed by Jan Lahoda
parent b588797900
commit 8aa50288a1
53 changed files with 1640 additions and 453 deletions

View File

@ -70,6 +70,8 @@ public @interface PreviewFeature {
FOREIGN,
@JEP(number=430, title="String Templates", status="First Preview")
STRING_TEMPLATES,
@JEP(number=443, title="Unnamed Patterns and Variables")
UNNAMED,
/**
* A key for testing.
*/

View File

@ -131,9 +131,10 @@ public interface Element extends javax.lang.model.AnnotatedConstruct {
* {@code java.util.Set<E>} is {@code "Set"}.
*
* If this element represents an unnamed {@linkplain
* PackageElement#getSimpleName package} or unnamed {@linkplain
* ModuleElement#getSimpleName module}, an {@linkplain
* Name##empty_name empty name} is returned.
* PackageElement#getSimpleName package}, an unnamed {@linkplain
* ModuleElement#getSimpleName module} or an unnamed {@linkplain
* VariableElement#getSimpleName variable}, an {@linkplain Name##empty_name empty name}
* is returned.
*
* If it represents a {@linkplain ExecutableElement#getSimpleName
* constructor}, the name "{@code <init>}" is returned. If it

View File

@ -25,6 +25,8 @@
package javax.lang.model.element;
import jdk.internal.javac.PreviewFeature;
import javax.lang.model.util.Elements;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeKind;
@ -81,6 +83,9 @@ public interface VariableElement extends Element {
* parameters of the same executable. If the original source
* names are not available, an implementation may synthesize names
* subject to the distinctness requirement above.
*
* <p>For variables, the name of each variable is returned, or an empty name
* if the variable is unnamed.
*/
@Override
Name getSimpleName();
@ -93,4 +98,21 @@ public interface VariableElement extends Element {
*/
@Override
Element getEnclosingElement();
/**
* {@return {@code true} if this is an unnamed variable and {@code
* false} otherwise}
*
* @implSpec
* The default implementation of this method calls {@code
* getSimpleName()} and returns {@code true} if the result is
* empty and {@code false} otherwise.
*
* @jls 6.1 Declarations
* @jls 14.4 Local Variable Declarations
*
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED, reflective = true)
default boolean isUnnamed() { return getSimpleName().isEmpty(); }
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2023, 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 jdk.internal.javac.PreviewFeature;
/**
* A tree node for a binding pattern that matches a pattern
* with a variable of any name and a type of the match candidate;
* an unnamed pattern.
*
* For example the use of underscore {@code _} below:
* <pre>
* if (r instanceof R(_)) {}
* </pre>
*
* @jls 14.30.1 Kinds of Patterns
*
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED)
public interface AnyPatternTree extends PatternTree {
}

View File

@ -227,6 +227,14 @@ public interface Tree {
*/
PARENTHESIZED(ParenthesizedTree.class),
/**
* Used for instances of {@link BindingPatternTree}.
*
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED)
ANY_PATTERN(AnyPatternTree.class),
/**
* Used for instances of {@link BindingPatternTree}.
*

View File

@ -268,6 +268,16 @@ public interface TreeVisitor<R,P> {
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES, reflective=true)
R visitStringTemplate(StringTemplateTree node, P p);
/**
* Visits a {@code AnyPatternTree} node.
* @param node the node being visited
* @param p a parameter value
* @return a result value
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED)
R visitAnyPattern(AnyPatternTree node, P p);
/**
* Visits a {@code BindingPatternTree} node.
* @param node the node being visited

View File

@ -51,7 +51,8 @@ public interface VariableTree extends StatementTree {
ModifiersTree getModifiers();
/**
* Returns the name of the variable being declared.
* Returns the name of the variable being declared or empty name if both the variable
* is unnamed and the preview features are enabled (Unnamed Patterns and Variables).
* @return the name
*/
Name getName();

View File

@ -641,6 +641,22 @@ 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 21
*/
@Override
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED)
public R visitAnyPattern(AnyPatternTree node, P p) {
return defaultAction(node, p);
}
/**
* {@inheritDoc}
*

View File

@ -760,6 +760,21 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
return r;
}
/**
* {@inheritDoc}
*
* @implSpec This implementation returns {@code null}.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
* @since 21
*/
@Override
public R visitAnyPattern(AnyPatternTree node, P p) {
return null;
}
/**
* {@inheritDoc}
*

View File

@ -210,7 +210,7 @@ public class Preview {
public boolean isPreview(Feature feature) {
return switch (feature) {
case STRING_TEMPLATES -> true;
case UNNAMED_VARIABLES -> true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true'
//for those selected features, and 'false' for all the others.

View File

@ -239,6 +239,7 @@ public enum Source {
RECORD_PATTERNS(JDK21, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL),
STRING_TEMPLATES(JDK21, Fragments.FeatureStringTemplates, DiagKind.PLURAL),
WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
UNNAMED_VARIABLES(JDK21, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL),
;
enum DiagKind {

View File

@ -1641,6 +1641,7 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
/** A class for variable symbols
*/
@SuppressWarnings("preview")
public static class VarSymbol extends Symbol implements VariableElement {
/** The variable's declaration position.
@ -1783,6 +1784,10 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
return v.visitVarSymbol(this, p);
}
public boolean isUnnamedVariable() {
return name.isEmpty();
}
}
public static class RecordComponent extends VarSymbol implements RecordComponentElement {

View File

@ -1688,7 +1688,7 @@ public class Attr extends JCTree.Visitor {
wasError = true;
}
MatchBindings currentBindings = null;
boolean wasUnconditionalPattern = hasUnconditionalPattern;
MatchBindings guardBindings = null;
for (List<JCCaseLabel> labels = c.labels; labels.nonEmpty(); labels = labels.tail) {
JCCaseLabel label = labels.head;
if (label instanceof JCConstantCaseLabel constLabel) {
@ -1761,7 +1761,7 @@ public class Attr extends JCTree.Visitor {
checkCastablePattern(pat.pos(), seltype, primaryType);
Type patternType = types.erasure(primaryType);
JCExpression guard = c.guard;
if (labels.tail.isEmpty() && guard != null) {
if (guardBindings == null && guard != null) {
MatchBindings afterPattern = matchBindings;
Env<AttrContext> bodyEnv = bindingEnv(switchEnv, matchBindings.bindingsWhenTrue);
try {
@ -1769,7 +1769,9 @@ public class Attr extends JCTree.Visitor {
} finally {
bodyEnv.info.scope.leave();
}
matchBindings = matchBindingsComputer.caseGuard(c, afterPattern, matchBindings);
guardBindings = matchBindings;
matchBindings = afterPattern;
if (TreeInfo.isBooleanWithValue(guard, 0)) {
log.error(guard.pos(), Errors.GuardHasConstantExpressionFalse);
@ -1796,6 +1798,10 @@ public class Attr extends JCTree.Visitor {
currentBindings = matchBindingsComputer.switchCase(label, currentBindings, matchBindings);
}
if (guardBindings != null) {
currentBindings = matchBindingsComputer.caseGuard(c, currentBindings, guardBindings);
}
Env<AttrContext> caseEnv =
bindingEnv(switchEnv, c, currentBindings.bindingsWhenTrue);
try {
@ -4145,6 +4151,11 @@ public class Attr extends JCTree.Visitor {
}
}
@Override
public void visitAnyPattern(JCAnyPattern tree) {
result = tree.type = resultInfo.pt;
}
public void visitBindingPattern(JCBindingPattern tree) {
Type type;
if (tree.var.vartype != null) {
@ -4166,7 +4177,11 @@ public class Attr extends JCTree.Visitor {
}
chk.validate(tree.var.vartype, env, true);
result = tree.type;
matchBindings = new MatchBindings(List.of(v), List.nil());
if (v.isUnnamedVariable()) {
matchBindings = MatchBindingsComputer.EMPTY;
} else {
matchBindings = new MatchBindings(List.of(v), List.nil());
}
}
@Override

View File

@ -4572,8 +4572,11 @@ public class Check {
}
}
}
} else {
if (c.labels.tail.nonEmpty()) {
} else if (c.labels.tail.nonEmpty()) {
var patterCaseLabels = c.labels.stream().filter(ll -> ll instanceof JCPatternCaseLabel).map(cl -> (JCPatternCaseLabel)cl);
var allUnderscore = patterCaseLabels.allMatch(pcl -> !hasBindings(pcl.getPattern()));
if (!allUnderscore) {
log.error(c.labels.tail.head.pos(), Errors.FlowsThroughFromPattern);
}
}
@ -4608,7 +4611,7 @@ public class Check {
new TreeScanner() {
@Override
public void visitBindingPattern(JCBindingPattern tree) {
bindings[0] = true;
bindings[0] = !tree.var.sym.isUnnamedVariable();
super.visitBindingPattern(tree);
}
}.scan(p);

View File

@ -742,87 +742,6 @@ public class Flow {
alive = alive.or(resolveYields(tree, prevPendingExits));
}
sealed interface PatternDescription {
public static PatternDescription from(Types types, Type selectorType, JCPattern pattern) {
if (pattern instanceof JCBindingPattern binding) {
Type type = types.isSubtype(selectorType, binding.type)
? selectorType : binding.type;
return new BindingPattern(type);
} else if (pattern instanceof JCRecordPattern record) {
Type[] componentTypes = ((ClassSymbol) record.type.tsym).getRecordComponents()
.map(r -> types.memberType(record.type, r))
.toArray(s -> new Type[s]);
PatternDescription[] nestedDescriptions =
new PatternDescription[record.nested.size()];
int i = 0;
for (List<JCPattern> it = record.nested;
it.nonEmpty();
it = it.tail, i++) {
nestedDescriptions[i] = PatternDescription.from(types, componentTypes[i], it.head);
}
return new RecordPattern(record.type, componentTypes, nestedDescriptions);
} else {
throw Assert.error();
}
}
}
record BindingPattern(Type type) implements PatternDescription {
@Override
public int hashCode() {
return type.tsym.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof BindingPattern other &&
type.tsym == other.type.tsym;
}
@Override
public String toString() {
return type.tsym + " _";
}
}
record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription... nested) implements PatternDescription {
public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested) {
this(recordType, hashCode(-1, recordType, nested), fullComponentTypes, nested);
}
@Override
public int hashCode() {
return _hashCode;
}
@Override
public boolean equals(Object o) {
return o instanceof RecordPattern other &&
recordType.tsym == other.recordType.tsym &&
Arrays.equals(nested, other.nested);
}
public int hashCode(int excludeComponent) {
return hashCode(excludeComponent, recordType, nested);
}
public static int hashCode(int excludeComponent, Type recordType, PatternDescription... nested) {
int hash = 5;
hash = 41 * hash + recordType.tsym.hashCode();
for (int i = 0; i < nested.length; i++) {
if (i != excludeComponent) {
hash = 41 * hash + nested[i].hashCode();
}
}
return hash;
}
@Override
public String toString() {
return recordType.tsym + "(" + Arrays.stream(nested)
.map(pd -> pd.toString())
.collect(Collectors.joining(", ")) + ")";
}
}
private boolean exhausts(JCExpression selector, List<JCCase> cases) {
Set<PatternDescription> patternSet = new HashSet<>();
Map<Symbol, Set<Symbol>> enum2Constants = new HashMap<>();
@ -833,7 +752,7 @@ public class Flow {
for (var l : c.labels) {
if (l instanceof JCPatternCaseLabel patternLabel) {
for (Type component : components(selector.type)) {
patternSet.add(PatternDescription.from(types, component, patternLabel.pat));
patternSet.add(makePatternDescription(component, patternLabel.pat));
}
} else if (l instanceof JCConstantCaseLabel constantLabel) {
Symbol s = TreeInfo.symbol(constantLabel.expr);
@ -2905,7 +2824,7 @@ public class Flow {
if (!resourceVarDecls.isEmpty() &&
lint.isEnabled(Lint.LintCategory.TRY)) {
for (JCVariableDecl resVar : resourceVarDecls) {
if (unrefdResources.includes(resVar.sym)) {
if (unrefdResources.includes(resVar.sym) && !resVar.sym.isUnnamedVariable()) {
log.warning(Lint.LintCategory.TRY, resVar.pos(),
Warnings.TryResourceNotReferenced(resVar.sym));
unrefdResources.remove(resVar.sym);
@ -3554,4 +3473,85 @@ public class Flow {
}
}
sealed interface PatternDescription { }
public PatternDescription makePatternDescription(Type selectorType, JCPattern pattern) {
if (pattern instanceof JCBindingPattern binding) {
Type type = types.isSubtype(selectorType, binding.type)
? selectorType : binding.type;
return new BindingPattern(type);
} else if (pattern instanceof JCRecordPattern record) {
Type[] componentTypes = ((ClassSymbol) record.type.tsym).getRecordComponents()
.map(r -> types.memberType(record.type, r))
.toArray(s -> new Type[s]);
PatternDescription[] nestedDescriptions =
new PatternDescription[record.nested.size()];
int i = 0;
for (List<JCPattern> it = record.nested;
it.nonEmpty();
it = it.tail, i++) {
nestedDescriptions[i] = makePatternDescription(types.erasure(componentTypes[i]), it.head);
}
return new RecordPattern(record.type, componentTypes, nestedDescriptions);
} else if (pattern instanceof JCAnyPattern) {
Type type = types.isSubtype(selectorType, syms.objectType)
? selectorType : syms.objectType;
return new BindingPattern(type);
} else {
throw Assert.error();
}
}
record BindingPattern(Type type) implements PatternDescription {
@Override
public int hashCode() {
return type.tsym.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof BindingPattern other &&
type.tsym == other.type.tsym;
}
@Override
public String toString() {
return type.tsym + " _";
}
}
record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription... nested) implements PatternDescription {
public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested) {
this(recordType, hashCode(-1, recordType, nested), fullComponentTypes, nested);
}
@Override
public int hashCode() {
return _hashCode;
}
@Override
public boolean equals(Object o) {
return o instanceof RecordPattern other &&
recordType.tsym == other.recordType.tsym &&
Arrays.equals(nested, other.nested);
}
public int hashCode(int excludeComponent) {
return hashCode(excludeComponent, recordType, nested);
}
public static int hashCode(int excludeComponent, Type recordType, PatternDescription... nested) {
int hash = 5;
hash = 41 * hash + recordType.tsym.hashCode();
for (int i = 0; i < nested.length; i++) {
if (i != excludeComponent) {
hash = 41 * hash + nested[i].hashCode();
}
}
return hash;
}
@Override
public String toString() {
return recordType.tsym + "(" + Arrays.stream(nested)
.map(pd -> pd.toString())
.collect(Collectors.joining(", ")) + ")";
}
}
}

View File

@ -34,6 +34,7 @@ import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.JCDiagnostic.Error;
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.*;
@ -55,6 +56,8 @@ import static com.sun.tools.javac.code.TypeTag.TYPEVAR;
public class MemberEnter extends JCTree.Visitor {
protected static final Context.Key<MemberEnter> memberEnterKey = new Context.Key<>();
/** The Source language setting. */
private final Source source;
private final Enter enter;
private final Log log;
private final Check chk;
@ -62,6 +65,7 @@ public class MemberEnter extends JCTree.Visitor {
private final Symtab syms;
private final Annotate annotate;
private final Types types;
private final Names names;
private final DeferredLintHandler deferredLintHandler;
public static MemberEnter instance(Context context) {
@ -81,6 +85,8 @@ public class MemberEnter extends JCTree.Visitor {
syms = Symtab.instance(context);
annotate = Annotate.instance(context);
types = Types.instance(context);
source = Source.instance(context);
names = Names.instance(context);
deferredLintHandler = DeferredLintHandler.instance(context);
}
@ -285,7 +291,8 @@ public class MemberEnter extends JCTree.Visitor {
Type vartype = tree.isImplicitlyTyped()
? env.info.scope.owner.kind == MTH ? Type.noType : syms.errType
: tree.vartype.type;
VarSymbol v = new VarSymbol(0, tree.name, vartype, enclScope.owner);
Name name = tree.name;
VarSymbol v = new VarSymbol(0, name, vartype, enclScope.owner);
v.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, v, tree);
tree.sym = v;
if (tree.init != null) {
@ -297,12 +304,15 @@ public class MemberEnter extends JCTree.Visitor {
v.setLazyConstValue(initEnv(tree, initEnv), attr, tree);
}
}
if (chk.checkUnique(tree.pos(), v, enclScope)) {
chk.checkTransparentVar(tree.pos(), v, enclScope);
enclScope.enter(v);
} else if (v.owner.kind == MTH || (v.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0) {
// if this is a parameter or a field obtained from a record component, enter it
enclScope.enter(v);
if(!(Feature.UNNAMED_VARIABLES.allowedInSource(source) && tree.sym.isUnnamedVariable())) {
if (chk.checkUnique(tree.pos(), v, enclScope)) {
chk.checkTransparentVar(tree.pos(), v, enclScope);
enclScope.enter(v);
} else if (v.owner.kind == MTH || (v.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0) {
// if this is a parameter or a field obtained from a record component, enter it
enclScope.enter(v);
}
}
annotate.annotateLater(tree.mods.annotations, localEnv, v, tree.pos());

View File

@ -45,6 +45,7 @@ import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.code.Type.WildcardType;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCAnyPattern;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCExpression;
@ -261,6 +262,11 @@ public class TransPatterns extends TreeTranslator {
}
}
@Override
public void visitAnyPattern(JCTree.JCAnyPattern that) {
result = make.Literal(true);
}
@Override
public void visitBindingPattern(JCBindingPattern tree) {
//it is assumed the primary type has already been checked:
@ -268,7 +274,7 @@ public class TransPatterns extends TreeTranslator {
Type castTargetType = types.erasure(TreeInfo.primaryPatternType(tree));
VarSymbol bindingVar = bindingContext.bindingDeclared(binding);
if (bindingVar != null) {
if (bindingVar != null && !bindingVar.isUnnamedVariable()) {
JCAssign fakeInit = (JCAssign)make.at(TreeInfo.getStartPos(tree)).Assign(
make.Ident(bindingVar), convert(make.Ident(currentValue), castTargetType)).setType(bindingVar.erasure(types));
LetExpr nestedLE = make.LetExpr(List.of(make.Exec(fakeInit)),
@ -311,7 +317,7 @@ public class TransPatterns extends TreeTranslator {
RecordComponent component = components.head;
Type componentType = types.erasure(nestedFullComponentTypes.head);
JCPattern nestedPattern = nestedPatterns.head;
JCBindingPattern nestedBinding;
JCPattern nestedBinding;
boolean allowNull;
if (nestedPattern instanceof JCRecordPattern nestedRecordPattern) {
UnrolledRecordPattern nestedDesugared = unrollRecordPattern(nestedRecordPattern);
@ -325,7 +331,11 @@ public class TransPatterns extends TreeTranslator {
}
nestedBinding = nestedDesugared.primaryPattern();
allowNull = false;
} else {
} else if (nestedPattern instanceof JCAnyPattern nestedAnyPattern) {
allowNull = true;
nestedBinding = nestedAnyPattern;
}
else {
nestedBinding = (JCBindingPattern) nestedPattern;
allowNull = types.isSubtype(componentType,
types.boxedTypeOrType(types.erasure(nestedBinding.type)));
@ -437,12 +447,11 @@ public class TransPatterns extends TreeTranslator {
if (pattern instanceof JCRecordPattern recordPattern) {
UnrolledRecordPattern deconstructed = unrollRecordPattern(recordPattern);
JCExpression guard = deconstructed.newGuard();
if (cse.guard != null) {
cse.guard = mergeConditions(guard, cse.guard);
} else {
cse.guard = guard;
}
return make.PatternCaseLabel(deconstructed.primaryPattern());
JCPatternCaseLabel newPatternCaseLabel = make.PatternCaseLabel(deconstructed.primaryPattern());
newPatternCaseLabel.syntheticGuard = guard;
return newPatternCaseLabel;
}
}
return l;
@ -524,23 +533,61 @@ public class TransPatterns extends TreeTranslator {
.filter(l -> !TreeInfo.isNullCaseLabel(l))
.collect(List.collector());
}
if (clearedPatterns.size() == 1 && clearedPatterns.head.hasTag(Tag.PATTERNCASELABEL) && !previousCompletesNormally) {
JCPatternCaseLabel label = (JCPatternCaseLabel) clearedPatterns.head;
boolean validCaseLabelList;
if (clearedPatterns.size() > 1) {
validCaseLabelList = clearedPatterns.stream().allMatch(cP -> cP.hasTag(Tag.PATTERNCASELABEL));
} else {
validCaseLabelList = clearedPatterns.head.hasTag(Tag.PATTERNCASELABEL);
}
if (validCaseLabelList && !previousCompletesNormally) {
List<JCPatternCaseLabel> labels = clearedPatterns.stream().map(cp -> (JCPatternCaseLabel)cp).collect(List.collector());
bindingContext = new BasicBindingContext();
VarSymbol prevCurrentValue = currentValue;
try {
currentValue = temp;
JCExpression test = (JCExpression) this.<JCTree>translate(label.pat);
if (c.guard != null) {
JCExpression guard = translate(c.guard);
test = makeBinary(Tag.AND, test, guard);
JCExpression test = null;
JCExpression accTest = null;
boolean first = true;
// if all patterns are the same we do not need an extra test for the first (it is covered by the BSM)
boolean multiplePatternsAndDifferent = labels.size() > 1 &&
!labels.stream().map(l -> l.pat.type.tsym).allMatch(labels.get(0).pat.type.tsym::equals);
for (JCPatternCaseLabel label: labels) {
test = (JCExpression) this.<JCTree>translate(label.pat);
if (multiplePatternsAndDifferent) {
test = makeBinary(Tag.AND, makeTypeTest(make.Ident(temp), make.Type(label.pat.type)), test);
}
if (label.syntheticGuard != null) {
JCExpression guard = translate(label.syntheticGuard);
test = makeBinary(Tag.AND, test, guard);
}
if (!first) {
accTest = makeBinary(Tag.OR, accTest, test);
} else {
accTest = test;
first = false;
}
}
if (c.guard != null) {
test = makeBinary(Tag.AND, accTest, translate(c.guard));
c.guard = null;
} else {
test = accTest;
}
c.stats = translate(c.stats);
JCContinue continueSwitch = make.at(clearedPatterns.head.pos()).Continue(null);
continueSwitch.target = tree;
c.stats = c.stats.prepend(make.If(makeUnary(Tag.NOT, test).setType(syms.booleanType),
make.Block(0, List.of(make.Exec(make.Assign(make.Ident(index),
makeLit(syms.intType, i + 1))
makeLit(syms.intType, i + labels.length()))
.setType(syms.intType)),
continueSwitch)),
null));
@ -556,16 +603,20 @@ public class TransPatterns extends TreeTranslator {
fixupContinue(tree, c, index, i);
ListBuffer<JCCaseLabel> translatedLabels = new ListBuffer<>();
for (var p : c.labels) {
List<JCCaseLabel> labels = c.labels;
boolean defaultAdded = false;
for (int j = 0; j < labels.size() && !defaultAdded; j++) {
var p = labels.get(j);
if (p.hasTag(Tag.DEFAULTCASELABEL)) {
translatedLabels.add(p);
hasDefault = true;
} else if (hasUnconditionalPattern && !hasDefault &&
c == lastCase && p.hasTag(Tag.PATTERNCASELABEL)) {
c == lastCase && p.hasTag(Tag.PATTERNCASELABEL)) {
//If the switch has unconditional pattern,
//the last case will contain it.
//Convert the unconditional pattern to default:
translatedLabels.add(make.DefaultCaseLabel());
defaultAdded = true;
} else {
int value;
if (TreeInfo.isNullCaseLabel(p)) {
@ -698,7 +749,7 @@ public class TransPatterns extends TreeTranslator {
"commonNestedExpression: " + commonNestedExpression +
"commonNestedBinding: " + commonNestedBinding);
ListBuffer<JCCase> nestedCases = new ListBuffer<>();
JCExpression lastGuard = null;
boolean hasGuard = false;
for(List<JCCase> accList = accummulator.toList(); accList.nonEmpty(); accList = accList.tail) {
var accummulated = accList.head;
@ -713,29 +764,33 @@ public class TransPatterns extends TreeTranslator {
replaceNested.scan(accummulated);
JCExpression newGuard;
JCInstanceOf instanceofCheck;
if (accummulated.guard instanceof JCBinary binOp) {
if (accummulatedFirstLabel.syntheticGuard instanceof JCBinary binOp) {
newGuard = binOp.rhs;
instanceofCheck = (JCInstanceOf) binOp.lhs;
} else {
newGuard = null;
instanceofCheck = (JCInstanceOf) accummulated.guard;
instanceofCheck = (JCInstanceOf) accummulatedFirstLabel.syntheticGuard;
}
JCBindingPattern binding = (JCBindingPattern) instanceofCheck.pattern;
hasUnconditional =
instanceofCheck.allowNull &&
accList.tail.isEmpty();
List<JCCaseLabel> newLabel;
JCPatternCaseLabel jcPatternCaseLabelWithGuard = make.PatternCaseLabel(binding);
jcPatternCaseLabelWithGuard.syntheticGuard = newGuard;
if (hasUnconditional) {
newLabel = List.of(make.ConstantCaseLabel(makeNull()),
make.PatternCaseLabel(binding));
jcPatternCaseLabelWithGuard);
} else {
newLabel = List.of(make.PatternCaseLabel(binding));
newLabel = List.of(jcPatternCaseLabelWithGuard);
}
nestedCases.add(make.Case(CaseKind.STATEMENT, newLabel, newGuard,
nestedCases.add(make.Case(CaseKind.STATEMENT, newLabel, accummulated.guard,
accummulated.stats, null));
lastGuard = newGuard;
hasGuard = newGuard != null || accummulated.guard != null;
}
if (lastGuard != null || !hasUnconditional) {
if (hasGuard || !hasUnconditional) {
JCContinue continueSwitch = make.Continue(null);
continueSwitch.target = currentSwitch;
nestedCases.add(make.Case(CaseKind.STATEMENT,
@ -751,6 +806,7 @@ public class TransPatterns extends TreeTranslator {
newSwitch.patternSwitch = true;
JCPatternCaseLabel leadingTest =
(JCPatternCaseLabel) accummulator.first().labels.head;
leadingTest.syntheticGuard = null;
result.add(make.Case(CaseKind.STATEMENT,
List.of(leadingTest),
null,
@ -775,14 +831,14 @@ public class TransPatterns extends TreeTranslator {
if (c.head.labels.size() == 1 &&
c.head.labels.head instanceof JCPatternCaseLabel patternLabel) {
if (c.head.guard instanceof JCBinary binOp &&
if (patternLabel.syntheticGuard instanceof JCBinary binOp &&
binOp.lhs instanceof JCInstanceOf instanceofCheck &&
instanceofCheck.pattern instanceof JCBindingPattern binding) {
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
currentNullable = instanceofCheck.allowNull;
currentNestedExpression = instanceofCheck.expr;
currentNestedBinding = binding.var.sym;
} else if (c.head.guard instanceof JCInstanceOf instanceofCheck &&
} else if (patternLabel.syntheticGuard instanceof JCInstanceOf instanceofCheck &&
instanceofCheck.pattern instanceof JCBindingPattern binding) {
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
currentNullable = instanceofCheck.allowNull;
@ -1373,5 +1429,11 @@ public class TransPatterns extends TreeTranslator {
tree.sym = fromTo.getOrDefault(tree.sym, tree.sym);
super.visitIdent(tree);
}
@Override
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
super.visitPatternCaseLabel(tree);
scan(tree.syntheticGuard);
}
}
}

View File

@ -556,6 +556,11 @@ public class TransTypes extends TreeTranslator {
result = tree;
}
@Override
public void visitAnyPattern(JCAnyPattern tree) {
result = tree;
}
public void visitBindingPattern(JCBindingPattern tree) {
tree.var = translate(tree.var, null);
result = tree;

View File

@ -62,7 +62,6 @@ import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Implici
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndExplicitNotAllowed;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndImplicitNotAllowed;
import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
import java.util.function.BiFunction;
/**
* The parser maps a token sequence into an abstract syntax tree.
@ -590,6 +589,14 @@ public class JavacParser implements Parser {
}
protected Name ident(boolean allowClass) {
return ident(allowClass, false);
}
public Name identOrUnderscore() {
return ident(false, true);
}
protected Name ident(boolean allowClass, boolean asVariable) {
if (token.kind == IDENTIFIER) {
Name name = token.name();
nextToken();
@ -615,8 +622,14 @@ public class JavacParser implements Parser {
} else if (token.kind == UNDERSCORE) {
if (Feature.UNDERSCORE_IDENTIFIER.allowedInSource(source)) {
log.warning(token.pos, Warnings.UnderscoreAsIdentifier);
} else if (asVariable) {
checkSourceLevel(Feature.UNNAMED_VARIABLES);
} else {
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.UnderscoreAsIdentifier);
if (preview.isEnabled() && Feature.UNNAMED_VARIABLES.allowedInSource(source)) {
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.UseOfUnderscoreNotAllowed);
} else {
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.UnderscoreAsIdentifier);
}
}
Name name = token.name();
nextToken();
@ -830,57 +843,65 @@ public class JavacParser implements Parser {
/** parses patterns.
*/
public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType,
boolean allowVar, boolean checkGuard) {
JCPattern pattern;
mods = mods != null ? mods : optFinal(0);
JCExpression e;
if (parsedType == null) {
boolean var = token.kind == IDENTIFIER && token.name() == names.var;
e = unannotatedType(allowVar, TYPE | NOLAMBDA);
if (var) {
e = null;
}
} else {
e = parsedType;
if (token.kind == UNDERSCORE && parsedType == null) {
nextToken();
pattern = toP(F.at(token.pos).AnyPattern());
}
if (token.kind == LPAREN) {
//deconstruction pattern:
checkSourceLevel(Feature.RECORD_PATTERNS);
ListBuffer<JCPattern> nested = new ListBuffer<>();
if (!peekToken(RPAREN)) {
do {
nextToken();
JCPattern nestedPattern = parsePattern(token.pos, null, null, true, false);
nested.append(nestedPattern);
} while (token.kind == COMMA);
} else {
nextToken();
}
accept(RPAREN);
pattern = toP(F.at(pos).RecordPattern(e, nested.toList()));
if (mods.annotations.nonEmpty()) {
log.error(mods.annotations.head.pos(), Errors.RecordPatternsAnnotationsNotAllowed);
}
new TreeScanner() {
@Override
public void visitAnnotatedType(JCAnnotatedType tree) {
log.error(tree.pos(), Errors.RecordPatternsAnnotationsNotAllowed);
else {
if (parsedType == null) {
boolean var = token.kind == IDENTIFIER && token.name() == names.var;
e = unannotatedType(allowVar, TYPE | NOLAMBDA);
if (var) {
e = null;
}
}.scan(e);
} else {
//type test pattern:
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
if (e == null) {
var.startPos = pos;
} else {
e = parsedType;
}
if (token.kind == LPAREN) {
//deconstruction pattern:
checkSourceLevel(Feature.RECORD_PATTERNS);
ListBuffer<JCPattern> nested = new ListBuffer<>();
if (!peekToken(RPAREN)) {
do {
nextToken();
JCPattern nestedPattern = parsePattern(token.pos, null, null, true, false);
nested.append(nestedPattern);
} while (token.kind == COMMA);
} else {
nextToken();
}
accept(RPAREN);
pattern = toP(F.at(pos).RecordPattern(e, nested.toList()));
if (mods.annotations.nonEmpty()) {
log.error(mods.annotations.head.pos(), Errors.RecordPatternsAnnotationsNotAllowed);
}
new TreeScanner() {
@Override
public void visitAnnotatedType(JCAnnotatedType tree) {
log.error(tree.pos(), Errors.RecordPatternsAnnotationsNotAllowed);
}
}.scan(e);
} else {
//type test pattern:
int varPos = token.pos;
JCVariableDecl var = variableDeclaratorRest(varPos, mods, e, identOrUnderscore(), false, null, false, false, true);
if (e == null) {
var.startPos = pos;
if (var.name == names.underscore && !allowVar) {
log.error(DiagnosticFlag.SYNTAX, varPos, Errors.UseOfUnderscoreNotAllowed);
}
}
pattern = toP(F.at(pos).BindingPattern(var));
}
pattern = toP(F.at(pos).BindingPattern(var));
}
return pattern;
}
/**
* parses (optional) type annotations followed by a type. If the
* annotations are present before the type and are not consumed during array
@ -1077,6 +1098,9 @@ public class JavacParser implements Parser {
pattern = parsePattern(patternPos, mods, type, false, false);
} else if (token.kind == LPAREN) {
pattern = parsePattern(patternPos, mods, type, false, false);
} else if (token.kind == UNDERSCORE) {
checkSourceLevel(token.pos, Feature.UNNAMED_VARIABLES);
pattern = parsePattern(patternPos, mods, type, false, false);
} else {
checkNoMods(typePos, mods.flags & ~Flags.DEPRECATED);
if (mods.annotations.nonEmpty()) {
@ -2052,23 +2076,31 @@ public class JavacParser implements Parser {
};
class LambdaClassifier {
LambdaParameterKind kind;
Fragment diagFragment;
List<JCVariableDecl> params;
/**
* analyzeParens() has already classified the lambda as EXPLICIT_LAMBDA, due to
* two consecutive identifiers. Because of that {@code (<explicit lambda>)}, the
* parser will always attempt to parse a type, followed by a name. If the lambda
* contains an illegal mix of implicit and explicit parameters, it is possible
* for the parser to see a {@code ,} when expecting a name, in which case the
* variable is created with an erroneous name. The logic below makes sure that
* the lambda parameters are all declared with either an explicit type (e.g.
* {@code String x}), or with an inferred type (using {@code var x}). Any other
* combination is rejected.
* */
void addParameter(JCVariableDecl param) {
if (param.vartype != null && param.name != names.empty) {
if (restrictedTypeName(param.vartype, false) != null) {
reduce(LambdaParameterKind.VAR);
} else {
reduce(LambdaParameterKind.EXPLICIT);
}
}
if (param.vartype == null && param.name != names.empty ||
param.vartype != null && param.name == names.empty) {
Assert.check(param.vartype != null);
if (param.name == names.error) {
reduce(LambdaParameterKind.IMPLICIT);
}
else if (restrictedTypeName(param.vartype, false) != null) {
reduce(LambdaParameterKind.VAR);
} else {
reduce(LambdaParameterKind.EXPLICIT);
}
}
private void reduce(LambdaParameterKind newKind) {
@ -3074,7 +3106,7 @@ public class JavacParser implements Parser {
JCExpression paramType = catchTypes.size() > 1 ?
toP(F.at(catchTypes.head.getStartPosition()).TypeUnion(catchTypes)) :
catchTypes.head;
JCVariableDecl formal = variableDeclaratorId(mods, paramType);
JCVariableDecl formal = variableDeclaratorId(mods, paramType, true, false, false);
accept(RPAREN);
JCBlock body = block();
return F.at(pos).Catch(formal, body);
@ -3240,7 +3272,7 @@ public class JavacParser implements Parser {
switch (token) {
case BYTE: case SHORT: case INT: case LONG: case FLOAT:
case DOUBLE: case BOOLEAN: case CHAR: case VOID:
case ASSERT, ENUM, IDENTIFIER, UNDERSCORE:
case ASSERT, ENUM, IDENTIFIER:
if (typeDepth == 0 && peekToken(lookahead, LAX_IDENTIFIER)) {
if (parenDepth == 0) {
return PatternResult.PATTERN;
@ -3249,6 +3281,18 @@ public class JavacParser implements Parser {
}
}
break;
case UNDERSCORE:
// TODO: REFACTOR to remove the code duplication
if (typeDepth == 0 && peekToken(lookahead, tk -> tk == RPAREN || tk == COMMA)) {
return PatternResult.PATTERN;
} else if (typeDepth == 0 && peekToken(lookahead, LAX_IDENTIFIER)) {
if (parenDepth == 0) {
return PatternResult.PATTERN;
} else {
pendingResult = PatternResult.PATTERN;
}
}
break;
case DOT, QUES, EXTENDS, SUPER, COMMA: break;
case LT: typeDepth++; break;
case GTGTGT: typeDepth--;
@ -3556,7 +3600,7 @@ public class JavacParser implements Parser {
T vdefs,
boolean localDecl)
{
return variableDeclaratorsRest(token.pos, mods, type, ident(), false, null, vdefs, localDecl);
return variableDeclaratorsRest(token.pos, mods, type, identOrUnderscore(), false, null, vdefs, localDecl);
}
/** VariableDeclaratorsRest = VariableDeclaratorRest { "," VariableDeclarator }
@ -3574,7 +3618,7 @@ public class JavacParser implements Parser {
T vdefs,
boolean localDecl)
{
JCVariableDecl head = variableDeclaratorRest(pos, mods, type, name, reqInit, dc, localDecl, false);
JCVariableDecl head = variableDeclaratorRest(pos, mods, type, name, reqInit, dc, localDecl, false, false);
vdefs.append(head);
while (token.kind == COMMA) {
// All but last of multiple declarators subsume a comma
@ -3589,7 +3633,7 @@ public class JavacParser implements Parser {
* ConstantDeclarator = Ident ConstantDeclaratorRest
*/
JCVariableDecl variableDeclarator(JCModifiers mods, JCExpression type, boolean reqInit, Comment dc, boolean localDecl) {
return variableDeclaratorRest(token.pos, mods, type, ident(), reqInit, dc, localDecl, true);
return variableDeclaratorRest(token.pos, mods, type, identOrUnderscore(), reqInit, dc, localDecl, true, false);
}
/** VariableDeclaratorRest = BracketsOpt ["=" VariableInitializer]
@ -3599,42 +3643,63 @@ public class JavacParser implements Parser {
* @param dc The documentation comment for the variable declarations, or null.
*/
JCVariableDecl variableDeclaratorRest(int pos, JCModifiers mods, JCExpression type, Name name,
boolean reqInit, Comment dc, boolean localDecl, boolean compound) {
boolean reqInit, Comment dc, boolean localDecl, boolean compound, boolean isTypePattern) {
boolean declaredUsingVar = false;
type = bracketsOpt(type);
JCExpression init = null;
if (Feature.UNNAMED_VARIABLES.allowedInSource(source) && name == names.underscore) {
if (!localDecl && !isTypePattern) {
log.error(DiagnosticFlag.SYNTAX, pos, Errors.UseOfUnderscoreNotAllowed);
}
name = names.empty;
}
if (token.kind == EQ) {
nextToken();
init = variableInitializer();
}
else if (reqInit) syntaxError(token.pos, Errors.Expected(EQ));
JCTree elemType = TreeInfo.innermostType(type, true);
int startPos = Position.NOPOS;
if (elemType.hasTag(IDENT)) {
Name typeName = ((JCIdent)elemType).name;
if (restrictedTypeNameStartingAtSource(typeName, pos, !compound && localDecl) != null) {
if (typeName != names.var) {
reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedHere(typeName));
} else if (type.hasTag(TYPEARRAY) && !compound) {
//error - 'var' and arrays
reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedArray(typeName));
} else {
declaredUsingVar = true;
if(compound)
//error - 'var' in compound local var decl
reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedCompound(typeName));
startPos = TreeInfo.getStartPos(mods);
if (startPos == Position.NOPOS)
startPos = TreeInfo.getStartPos(type);
//implicit type
type = null;
if (Feature.UNNAMED_VARIABLES.allowedInSource(source) && name == names.empty
&& localDecl
&& init == null
&& token.kind != COLON) { // if its unnamed local variable, it needs to have an init unless in enhanced-for
syntaxError(token.pos, Errors.Expected(EQ));
}
JCVariableDecl result;
if (!isTypePattern) {
int startPos = Position.NOPOS;
JCTree elemType = TreeInfo.innermostType(type, true);
if (elemType.hasTag(IDENT)) {
Name typeName = ((JCIdent) elemType).name;
if (restrictedTypeNameStartingAtSource(typeName, pos, !compound && localDecl) != null) {
if (typeName != names.var) {
reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedHere(typeName));
} else if (type.hasTag(TYPEARRAY) && !compound) {
//error - 'var' and arrays
reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedArray(typeName));
} else {
declaredUsingVar = true;
if (compound)
//error - 'var' in compound local var decl
reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedCompound(typeName));
startPos = TreeInfo.getStartPos(mods);
if (startPos == Position.NOPOS)
startPos = TreeInfo.getStartPos(type);
//implicit type
type = null;
}
}
}
result = toP(F.at(pos).VarDef(mods, name, type, init, declaredUsingVar));
attach(result, dc);
result.startPos = startPos;
} else {
result = toP(F.at(pos).VarDef(mods, name, type, null));
}
JCVariableDecl result =
toP(F.at(pos).VarDef(mods, name, type, init, declaredUsingVar));
attach(result, dc);
result.startPos = startPos;
return result;
}
@ -3690,55 +3755,50 @@ public class JavacParser implements Parser {
/** VariableDeclaratorId = Ident BracketsOpt
*/
JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type) {
return variableDeclaratorId(mods, type, false, false);
}
//where
JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type, boolean lambdaParameter, boolean recordComponent) {
JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type, boolean catchParameter, boolean lambdaParameter, boolean recordComponent) {
int pos = token.pos;
Name name;
if (lambdaParameter && token.kind == UNDERSCORE) {
log.error(pos, Errors.UnderscoreAsIdentifierInLambda);
name = token.name();
nextToken();
} else {
if (allowThisIdent ||
!lambdaParameter ||
LAX_IDENTIFIER.test(token.kind) ||
mods.flags != Flags.PARAMETER ||
mods.annotations.nonEmpty()) {
JCExpression pn = qualident(false);
if (pn.hasTag(Tag.IDENT) && ((JCIdent)pn).name != names._this) {
name = ((JCIdent)pn).name;
} else if (lambdaParameter && type == null) {
// we have a lambda parameter that is not an identifier this is a syntax error
type = pn;
name = names.empty;
reportSyntaxError(pos, Errors.Expected(IDENTIFIER));
} else {
if (allowThisIdent) {
if ((mods.flags & Flags.VARARGS) != 0) {
log.error(token.pos, Errors.VarargsAndReceiver);
}
if (token.kind == LBRACKET) {
log.error(token.pos, Errors.ArrayAndReceiver);
}
if (pn.hasTag(Tag.SELECT) && ((JCFieldAccess)pn).name != names._this) {
log.error(token.pos, Errors.WrongReceiver);
}
}
return toP(F.at(pos).ReceiverVarDef(mods, pn, type));
}
if (allowThisIdent ||
!lambdaParameter ||
LAX_IDENTIFIER.test(token.kind) ||
mods.flags != Flags.PARAMETER ||
mods.annotations.nonEmpty()) {
JCExpression pn;
if (token.kind == UNDERSCORE && (catchParameter || lambdaParameter)) {
pn = toP(F.at(token.pos).Ident(identOrUnderscore()));
} else {
/** if it is a lambda parameter and the token kind is not an identifier,
* and there are no modifiers or annotations, then this means that the compiler
* supposed the lambda to be explicit but it can contain a mix of implicit,
* var or explicit parameters. So we assign the error name to the parameter name
* instead of issuing an error and analyze the lambda parameters as a whole at
* a higher level.
*/
name = names.empty;
pn = qualident(false);
}
if (pn.hasTag(Tag.IDENT) && ((JCIdent)pn).name != names._this) {
name = ((JCIdent)pn).name;
} else if (lambdaParameter && type == null) {
// we have a lambda parameter that is not an identifier this is a syntax error
type = pn;
name = names.empty;
reportSyntaxError(pos, Errors.Expected(IDENTIFIER));
} else {
if (allowThisIdent) {
if ((mods.flags & Flags.VARARGS) != 0) {
log.error(token.pos, Errors.VarargsAndReceiver);
}
if (token.kind == LBRACKET) {
log.error(token.pos, Errors.ArrayAndReceiver);
}
if (pn.hasTag(Tag.SELECT) && ((JCFieldAccess)pn).name != names._this) {
log.error(token.pos, Errors.WrongReceiver);
}
}
return toP(F.at(pos).ReceiverVarDef(mods, pn, type));
}
} else {
/** if it is a lambda parameter and the token kind is not an identifier,
* and there are no modifiers or annotations, then this means that the compiler
* supposed the lambda to be explicit but it can contain a mix of implicit,
* var or explicit parameters. So we assign the error name to the parameter name
* instead of issuing an error and analyze the lambda parameters as a whole at
* a higher level.
*/
name = names.error;
}
if ((mods.flags & Flags.VARARGS) != 0 &&
token.kind == LBRACKET) {
@ -3749,6 +3809,10 @@ public class JavacParser implements Parser {
}
type = bracketsOpt(type);
if (Feature.UNNAMED_VARIABLES.allowedInSource(source) && name == names.underscore) {
name = names.empty;
}
return toP(F.at(pos).VarDef(mods, name, type, null,
type != null && type.hasTag(IDENT) && ((JCIdent)type).name == names.var));
}
@ -3779,12 +3843,12 @@ public class JavacParser implements Parser {
if (token.kind == FINAL || token.kind == MONKEYS_AT) {
JCModifiers mods = optFinal(0);
JCExpression t = parseType(true);
return variableDeclaratorRest(token.pos, mods, t, ident(), true, null, true, false);
return variableDeclaratorRest(token.pos, mods, t, ident(), true, null, true, false, false);
}
JCExpression t = term(EXPR | TYPE);
if (wasTypeMode() && LAX_IDENTIFIER.test(token.kind)) {
JCModifiers mods = F.Modifiers(0);
return variableDeclaratorRest(token.pos, mods, t, ident(), true, null, true, false);
return variableDeclaratorRest(token.pos, mods, t, identOrUnderscore(), true, null, true, false, false);
} else {
checkSourceLevel(Feature.EFFECTIVELY_FINAL_VARIABLES_IN_TRY_WITH_RESOURCES);
if (!t.hasTag(IDENT) && !t.hasTag(SELECT)) {
@ -4936,12 +5000,12 @@ public class JavacParser implements Parser {
}
typeAnnotationsPushedBack = List.nil();
}
return variableDeclaratorId(mods, type, lambdaParameter, recordComponent);
return variableDeclaratorId(mods, type, false, lambdaParameter, recordComponent);
}
protected JCVariableDecl implicitParameter() {
JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER);
return variableDeclaratorId(mods, null, true, false);
return variableDeclaratorId(mods, null, false, true, false);
}
/* ---------- auxiliary methods -------------- */

View File

@ -297,7 +297,7 @@ public class PrintingProcessor extends AbstractProcessor {
if (kind == ENUM_CONSTANT)
writer.print(e.getSimpleName());
else {
writer.print(e.asType().toString() + " " + e.getSimpleName() );
writer.print(e.asType().toString() + " " + (e.getSimpleName().isEmpty() ? "_" : e.getSimpleName()));
Object constantValue = e.getConstantValue();
if (constantValue != null) {
writer.print(" = ");

View File

@ -3131,6 +3131,9 @@ compiler.misc.feature.reifiable.types.instanceof=\
compiler.misc.feature.deconstruction.patterns=\
deconstruction patterns
compiler.misc.feature.unnamed.variables=\
unnamed variables
compiler.misc.feature.records=\
records
@ -3155,9 +3158,9 @@ compiler.warn.underscore.as.identifier=\
compiler.err.underscore.as.identifier=\
as of release 9, ''_'' is a keyword, and may not be used as an identifier
compiler.err.underscore.as.identifier.in.lambda=\
''_'' used as an identifier\n\
(use of ''_'' as an identifier is forbidden for lambda parameters)
compiler.err.use.of.underscore.not.allowed=\
as of release 21, the underscore keyword ''_'' is only allowed to declare\n\
unnamed patterns, local variables, exception parameters or lambda parameters
compiler.err.enum.as.identifier=\
as of release 5, ''enum'' is a keyword, and may not be used as an identifier

View File

@ -239,6 +239,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
/** Patterns.
*/
ANYPATTERN,
BINDINGPATTERN,
RECORDPATTERN,
@ -2263,6 +2264,34 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
implements PatternTree {
}
public static class JCAnyPattern extends JCPattern
implements AnyPatternTree {
protected JCAnyPattern() {
}
@Override
public void accept(Visitor v) {
v.visitAnyPattern(this);
}
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() {
return Kind.ANY_PATTERN;
}
@Override
@DefinedBy(Api.COMPILER_TREE)
public <R, D> R accept(TreeVisitor<R, D> v, D d) {
return v.visitAnyPattern(this, d);
}
@Override
public Tag getTag() {
return ANYPATTERN;
}
}
public static class JCBindingPattern extends JCPattern
implements BindingPatternTree {
public JCVariableDecl var;
@ -2368,6 +2397,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
implements PatternCaseLabelTree {
public JCPattern pat;
public JCExpression syntheticGuard;
protected JCPatternCaseLabel(JCPattern pat) {
this.pat = pat;
@ -3545,6 +3575,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public void visitBinary(JCBinary that) { visitTree(that); }
public void visitTypeCast(JCTypeCast that) { visitTree(that); }
public void visitTypeTest(JCInstanceOf that) { visitTree(that); }
public void visitAnyPattern(JCAnyPattern that) { visitTree(that); }
public void visitBindingPattern(JCBindingPattern that) { visitTree(that); }
public void visitDefaultCaseLabel(JCDefaultCaseLabel that) { visitTree(that); }
public void visitConstantCaseLabel(JCConstantCaseLabel that) { visitTree(that); }

View File

@ -715,7 +715,11 @@ public class Pretty extends JCTree.Visitor {
} else {
printExpr(tree.vartype);
print(' ');
print(tree.name);
if (tree.name.isEmpty()) {
print('_');
} else {
print(tree.name);
}
}
if (tree.init != null) {
print(" = ");
@ -941,6 +945,14 @@ public class Pretty extends JCTree.Visitor {
}
}
public void visitAnyPattern(JCAnyPattern patt) {
try {
print('_');
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public void visitRecordPattern(JCRecordPattern tree) {
try {

View File

@ -499,6 +499,12 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
return M.at(t.pos).TypeTest(expr, pattern);
}
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitAnyPattern(AnyPatternTree node, P p) {
JCAnyPattern t = (JCAnyPattern) node;
return M.at(t.pos).AnyPattern();
}
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitBindingPattern(BindingPatternTree node, P p) {
JCBindingPattern t = (JCBindingPattern) node;

View File

@ -1346,6 +1346,7 @@ public class TreeInfo {
return switch (pat.getTag()) {
case BINDINGPATTERN -> pat.type;
case RECORDPATTERN -> ((JCRecordPattern) pat).type;
case ANYPATTERN -> ((JCAnyPattern) pat).type;
default -> throw new AssertionError();
};
}

View File

@ -483,6 +483,12 @@ public class TreeMaker implements JCTree.Factory {
return tree;
}
public JCAnyPattern AnyPattern() {
JCAnyPattern tree = new JCAnyPattern();
tree.pos = pos;
return tree;
}
public JCBindingPattern BindingPattern(JCVariableDecl var) {
JCBindingPattern tree = new JCBindingPattern(var);
tree.pos = pos;

View File

@ -27,6 +27,7 @@ package com.sun.tools.javac.tree;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.tree.JCTree.*;
import jdk.internal.javac.PreviewFeature;
/** A subclass of Tree.Visitor, this class defines
* a general tree scanner pattern. Translation proceeds recursively in
@ -322,6 +323,11 @@ public class TreeScanner extends Visitor {
scan(tree.pat);
}
@Override
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED)
public void visitAnyPattern(JCAnyPattern that) {
}
@Override
public void visitRecordPattern(JCRecordPattern that) {
scan(that.deconstructor);

View File

@ -364,6 +364,10 @@ public class TreeTranslator extends JCTree.Visitor {
result = tree;
}
public void visitAnyPattern(JCAnyPattern tree) {
result = tree;
}
@Override
public void visitDefaultCaseLabel(JCDefaultCaseLabel tree) {
result = tree;

View File

@ -69,6 +69,7 @@ public class Names {
public final Name transitive;
public final Name uses;
public final Name open;
public final Name underscore;
public final Name when;
public final Name with;
public final Name yield;
@ -262,6 +263,7 @@ public class Names {
transitive = fromString("transitive");
uses = fromString("uses");
open = fromString("open");
underscore = fromString("_");
when = fromString("when");
with = fromString("with");
yield = fromString("yield");

View File

@ -0,0 +1,22 @@
/*
* @test /nodynamiccopyright/
* @bug 8304246
* @summary Compiler Implementation for Unnamed patterns and variables
* @enablePreview
* @compile/ref=TwrLintUnderscore.out --enable-preview -source ${jdk.version} -Xlint:try -XDrawDiagnostics TwrLintUnderscore.java
*/
class TwrLintUnderscore implements AutoCloseable {
private static void test1() {
try(TwrLintUnderscore _ = new TwrLintUnderscore()) {
// _ cannot be referenced so no lint warning for an unused resource should be emitted
}
}
/**
* The AutoCloseable method of a resource.
*/
@Override
public void close () {
return;
}
}

View File

@ -0,0 +1,2 @@
- compiler.note.preview.filename: TwrLintUnderscore.java, DEFAULT
- compiler.note.preview.recompile

View File

@ -148,7 +148,7 @@ public class TestGetScopeResult {
multipleCandidates2);
String[] implicitExplicitConflict1 = {
":t",
"<error>:t",
"s:java.lang.String",
"super:java.lang.Object",
"this:Test"
@ -159,7 +159,7 @@ public class TestGetScopeResult {
String[] implicitExplicitConflict2 = {
"s:none",
":t",
"<error>:t",
"super:java.lang.Object",
"this:Test"
};
@ -169,7 +169,7 @@ public class TestGetScopeResult {
String[] noFunctionInterface = {
"s:none",
":t",
"<error>:t",
"super:java.lang.Object",
"this:Test"
};

View File

@ -21,7 +21,10 @@
* questions.
*/
// key: compiler.err.underscore.as.identifier.in.lambda
// key: compiler.misc.feature.unnamed.variables
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
public class UnderscoreInLambdaExpression {
java.util.function.Function<String,String> f = _ -> "x";
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2023, 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.
*/
// key: compiler.err.use.of.underscore.not.allowed
// options: --enable-preview -source ${jdk.version} -Xlint:preview
import java.util.function.*;
class UseOfUnderscoreNotAllowed {
IntBinaryOperator f = (int x, int y) -> _ + x;
}

View File

@ -5,6 +5,7 @@
* @summary Test generation of warnings when '_' is used an identifier
* @compile/fail/ref=IdentifierTest8.out --release 8 -Werror -XDrawDiagnostics -Xlint:-options IdentifierTest.java
* @compile/fail/ref=IdentifierTest9.out -XDrawDiagnostics IdentifierTest.java
* @compile/fail/ref=IdentifierTest21.out -source ${jdk.version} --enable-preview -XDrawDiagnostics IdentifierTest.java
*/
import java.util.List;

View File

@ -0,0 +1,39 @@
IdentifierTest.java:42:11: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:45:16: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:47:22: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:52:13: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:52:23: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:54:13: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:56:13: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:64:67: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:71:13: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:72:14: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:73:18: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:80:13: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:80:15: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:82:13: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:82:15: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:89:10: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:89:38: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:95:14: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:102:17: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:102:26: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:119:20: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:124:10: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:129:17: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:132:17: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:139:24: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:139:33: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:140:39: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:145:15: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:146:13: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:151:15: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:152:17: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:160:25: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:169:5: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:173:26: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:175:19: compiler.err.use.of.underscore.not.allowed
IdentifierTest.java:181:11: compiler.err.use.of.underscore.not.allowed
- compiler.note.preview.filename: IdentifierTest.java, DEFAULT
- compiler.note.preview.recompile
36 errors

View File

@ -1,47 +1,47 @@
IdentifierTest.java:41:11: compiler.warn.underscore.as.identifier
IdentifierTest.java:44:16: compiler.warn.underscore.as.identifier
IdentifierTest.java:45:20: compiler.warn.underscore.as.identifier
IdentifierTest.java:46:22: compiler.warn.underscore.as.identifier
IdentifierTest.java:51:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:51:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:51:23: compiler.warn.underscore.as.identifier
IdentifierTest.java:53:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:55:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:61:21: compiler.warn.underscore.as.identifier
IdentifierTest.java:62:42: compiler.warn.underscore.as.identifier
IdentifierTest.java:63:67: compiler.warn.underscore.as.identifier
IdentifierTest.java:70:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:71:14: compiler.warn.underscore.as.identifier
IdentifierTest.java:72:18: compiler.warn.underscore.as.identifier
IdentifierTest.java:77:22: compiler.warn.underscore.as.identifier
IdentifierTest.java:79:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:79:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:81:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:81:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:88:10: compiler.warn.underscore.as.identifier
IdentifierTest.java:88:38: compiler.warn.underscore.as.identifier
IdentifierTest.java:94:14: compiler.warn.underscore.as.identifier
IdentifierTest.java:101:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:101:26: compiler.warn.underscore.as.identifier
IdentifierTest.java:118:20: compiler.warn.underscore.as.identifier
IdentifierTest.java:123:10: compiler.warn.underscore.as.identifier
IdentifierTest.java:128:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:131:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:138:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:138:24: compiler.warn.underscore.as.identifier
IdentifierTest.java:138:33: compiler.warn.underscore.as.identifier
IdentifierTest.java:139:39: compiler.warn.underscore.as.identifier
IdentifierTest.java:143:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:144:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:145:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:150:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:151:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:157:16: compiler.warn.underscore.as.identifier
IdentifierTest.java:159:25: compiler.warn.underscore.as.identifier
IdentifierTest.java:168:5: compiler.warn.underscore.as.identifier
IdentifierTest.java:172:26: compiler.warn.underscore.as.identifier
IdentifierTest.java:174:19: compiler.warn.underscore.as.identifier
IdentifierTest.java:180:11: compiler.warn.underscore.as.identifier
IdentifierTest.java:42:11: compiler.warn.underscore.as.identifier
IdentifierTest.java:45:16: compiler.warn.underscore.as.identifier
IdentifierTest.java:46:20: compiler.warn.underscore.as.identifier
IdentifierTest.java:47:22: compiler.warn.underscore.as.identifier
IdentifierTest.java:52:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:52:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:52:23: compiler.warn.underscore.as.identifier
IdentifierTest.java:54:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:56:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:62:21: compiler.warn.underscore.as.identifier
IdentifierTest.java:63:42: compiler.warn.underscore.as.identifier
IdentifierTest.java:64:67: compiler.warn.underscore.as.identifier
IdentifierTest.java:71:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:72:14: compiler.warn.underscore.as.identifier
IdentifierTest.java:73:18: compiler.warn.underscore.as.identifier
IdentifierTest.java:78:22: compiler.warn.underscore.as.identifier
IdentifierTest.java:80:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:80:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:82:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:82:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:89:10: compiler.warn.underscore.as.identifier
IdentifierTest.java:89:38: compiler.warn.underscore.as.identifier
IdentifierTest.java:95:14: compiler.warn.underscore.as.identifier
IdentifierTest.java:102:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:102:26: compiler.warn.underscore.as.identifier
IdentifierTest.java:119:20: compiler.warn.underscore.as.identifier
IdentifierTest.java:124:10: compiler.warn.underscore.as.identifier
IdentifierTest.java:129:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:132:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:139:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:139:24: compiler.warn.underscore.as.identifier
IdentifierTest.java:139:33: compiler.warn.underscore.as.identifier
IdentifierTest.java:140:39: compiler.warn.underscore.as.identifier
IdentifierTest.java:144:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:145:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:146:13: compiler.warn.underscore.as.identifier
IdentifierTest.java:151:15: compiler.warn.underscore.as.identifier
IdentifierTest.java:152:17: compiler.warn.underscore.as.identifier
IdentifierTest.java:158:16: compiler.warn.underscore.as.identifier
IdentifierTest.java:160:25: compiler.warn.underscore.as.identifier
IdentifierTest.java:169:5: compiler.warn.underscore.as.identifier
IdentifierTest.java:173:26: compiler.warn.underscore.as.identifier
IdentifierTest.java:175:19: compiler.warn.underscore.as.identifier
IdentifierTest.java:181:11: compiler.warn.underscore.as.identifier
- compiler.err.warnings.and.werror
1 error
44 warnings
44 warnings

View File

@ -1,45 +1,38 @@
IdentifierTest.java:41:11: compiler.err.underscore.as.identifier
IdentifierTest.java:44:16: compiler.err.underscore.as.identifier
IdentifierTest.java:45:20: compiler.err.underscore.as.identifier
IdentifierTest.java:46:22: compiler.err.underscore.as.identifier
IdentifierTest.java:51:13: compiler.err.underscore.as.identifier
IdentifierTest.java:51:15: compiler.err.underscore.as.identifier
IdentifierTest.java:51:23: compiler.err.underscore.as.identifier
IdentifierTest.java:53:13: compiler.err.underscore.as.identifier
IdentifierTest.java:55:13: compiler.err.underscore.as.identifier
IdentifierTest.java:61:21: compiler.err.underscore.as.identifier
IdentifierTest.java:62:42: compiler.err.underscore.as.identifier
IdentifierTest.java:63:67: compiler.err.underscore.as.identifier
IdentifierTest.java:70:13: compiler.err.underscore.as.identifier
IdentifierTest.java:71:14: compiler.err.underscore.as.identifier
IdentifierTest.java:72:18: compiler.err.underscore.as.identifier
IdentifierTest.java:77:22: compiler.err.underscore.as.identifier
IdentifierTest.java:79:13: compiler.err.underscore.as.identifier
IdentifierTest.java:79:15: compiler.err.underscore.as.identifier
IdentifierTest.java:81:13: compiler.err.underscore.as.identifier
IdentifierTest.java:81:15: compiler.err.underscore.as.identifier
IdentifierTest.java:88:10: compiler.err.underscore.as.identifier
IdentifierTest.java:88:38: compiler.err.underscore.as.identifier
IdentifierTest.java:94:14: compiler.err.underscore.as.identifier
IdentifierTest.java:101:17: compiler.err.underscore.as.identifier
IdentifierTest.java:101:26: compiler.err.underscore.as.identifier
IdentifierTest.java:118:20: compiler.err.underscore.as.identifier
IdentifierTest.java:123:10: compiler.err.underscore.as.identifier
IdentifierTest.java:128:17: compiler.err.underscore.as.identifier
IdentifierTest.java:131:17: compiler.err.underscore.as.identifier
IdentifierTest.java:138:17: compiler.err.underscore.as.identifier
IdentifierTest.java:138:24: compiler.err.underscore.as.identifier
IdentifierTest.java:138:33: compiler.err.underscore.as.identifier
IdentifierTest.java:139:39: compiler.err.underscore.as.identifier
IdentifierTest.java:143:13: compiler.err.underscore.as.identifier
IdentifierTest.java:144:15: compiler.err.underscore.as.identifier
IdentifierTest.java:145:13: compiler.err.underscore.as.identifier
IdentifierTest.java:150:15: compiler.err.underscore.as.identifier
IdentifierTest.java:151:17: compiler.err.underscore.as.identifier
IdentifierTest.java:157:16: compiler.err.underscore.as.identifier
IdentifierTest.java:159:25: compiler.err.underscore.as.identifier
IdentifierTest.java:168:5: compiler.err.underscore.as.identifier
IdentifierTest.java:172:26: compiler.err.underscore.as.identifier
IdentifierTest.java:174:19: compiler.err.underscore.as.identifier
IdentifierTest.java:180:11: compiler.err.underscore.as.identifier
44 errors
IdentifierTest.java:42:11: compiler.err.underscore.as.identifier
IdentifierTest.java:45:16: compiler.err.underscore.as.identifier
IdentifierTest.java:46:20: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.unnamed.variables)
IdentifierTest.java:47:22: compiler.err.underscore.as.identifier
IdentifierTest.java:52:13: compiler.err.underscore.as.identifier
IdentifierTest.java:52:23: compiler.err.underscore.as.identifier
IdentifierTest.java:54:13: compiler.err.underscore.as.identifier
IdentifierTest.java:56:13: compiler.err.underscore.as.identifier
IdentifierTest.java:64:67: compiler.err.underscore.as.identifier
IdentifierTest.java:71:13: compiler.err.underscore.as.identifier
IdentifierTest.java:72:14: compiler.err.underscore.as.identifier
IdentifierTest.java:73:18: compiler.err.underscore.as.identifier
IdentifierTest.java:80:13: compiler.err.underscore.as.identifier
IdentifierTest.java:80:15: compiler.err.underscore.as.identifier
IdentifierTest.java:82:13: compiler.err.underscore.as.identifier
IdentifierTest.java:82:15: compiler.err.underscore.as.identifier
IdentifierTest.java:89:10: compiler.err.underscore.as.identifier
IdentifierTest.java:89:38: compiler.err.underscore.as.identifier
IdentifierTest.java:95:14: compiler.err.underscore.as.identifier
IdentifierTest.java:102:17: compiler.err.underscore.as.identifier
IdentifierTest.java:102:26: compiler.err.underscore.as.identifier
IdentifierTest.java:119:20: compiler.err.underscore.as.identifier
IdentifierTest.java:124:10: compiler.err.underscore.as.identifier
IdentifierTest.java:129:17: compiler.err.underscore.as.identifier
IdentifierTest.java:132:17: compiler.err.underscore.as.identifier
IdentifierTest.java:139:24: compiler.err.underscore.as.identifier
IdentifierTest.java:139:33: compiler.err.underscore.as.identifier
IdentifierTest.java:140:39: compiler.err.underscore.as.identifier
IdentifierTest.java:145:15: compiler.err.underscore.as.identifier
IdentifierTest.java:146:13: compiler.err.underscore.as.identifier
IdentifierTest.java:151:15: compiler.err.underscore.as.identifier
IdentifierTest.java:152:17: compiler.err.underscore.as.identifier
IdentifierTest.java:160:25: compiler.err.underscore.as.identifier
IdentifierTest.java:169:5: compiler.err.underscore.as.identifier
IdentifierTest.java:173:26: compiler.err.underscore.as.identifier
IdentifierTest.java:175:19: compiler.err.underscore.as.identifier
IdentifierTest.java:181:11: compiler.err.underscore.as.identifier
37 errors

View File

@ -3,6 +3,7 @@
* @summary Check usages of underscore as identifier generate warnings
* @compile/fail/ref=UnderscoreAsIdent8.out --release 8 -XDrawDiagnostics -Xlint:-options -Werror UnderscoreAsIdent.java
* @compile/fail/ref=UnderscoreAsIdent9.out -XDrawDiagnostics -Werror UnderscoreAsIdent.java
* @compile/fail/ref=UnderscoreAsIdent21.out -source ${jdk.version} --enable-preview -XDrawDiagnostics UnderscoreAsIdent.java
*/
package _._;

View File

@ -0,0 +1,17 @@
UnderscoreAsIdent.java:8:9: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:8:11: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:10:8: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:10:10: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:12:7: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:13:12: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:14:10: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:14:19: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:19:25: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:19:33: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:25:9: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:27:19: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:29:9: compiler.err.use.of.underscore.not.allowed
UnderscoreAsIdent.java:31:22: compiler.err.use.of.underscore.not.allowed
- compiler.note.preview.filename: UnderscoreAsIdent.java, DEFAULT
- compiler.note.preview.recompile
14 errors

View File

@ -1,20 +1,20 @@
UnderscoreAsIdent.java:7:9: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:7:11: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:9:8: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:9:10: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:11:7: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:12:12: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:13:10: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:13:19: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:15:16: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:18:18: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:18:25: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:18:33: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:21:34: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:24:9: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:26:19: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:28:9: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:30:22: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:8:9: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:8:11: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:10:8: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:10:10: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:12:7: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:13:12: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:14:10: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:14:19: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:16:16: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:19:18: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:19:25: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:19:33: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:22:34: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:25:9: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:27:19: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:29:9: compiler.warn.underscore.as.identifier
UnderscoreAsIdent.java:31:22: compiler.warn.underscore.as.identifier
- compiler.err.warnings.and.werror
1 error
17 warnings

View File

@ -1,18 +1,16 @@
UnderscoreAsIdent.java:7:9: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:7:11: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:9:8: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:9:10: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:11:7: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:12:12: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:13:10: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:13:19: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:15:16: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:18:18: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:18:25: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:18:33: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:21:34: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:24:9: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:26:19: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:28:9: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:30:22: compiler.err.underscore.as.identifier
17 errors
UnderscoreAsIdent.java:8:9: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:8:11: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:10:8: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:10:10: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:12:7: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:13:12: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:14:10: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:14:19: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:16:16: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.unnamed.variables)
UnderscoreAsIdent.java:19:25: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:19:33: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:25:9: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:27:19: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:29:9: compiler.err.underscore.as.identifier
UnderscoreAsIdent.java:31:22: compiler.err.underscore.as.identifier
15 errors

View File

@ -12,7 +12,6 @@ public class DeconstructionPatternErrors {
public static void meth() throws Throwable {
Object p;
p = new P(42);
if (p instanceof P(_));
if (p instanceof P3(ArrayList<Integer> l));
if (p instanceof P4(ArrayList<Integer> l));
if (p instanceof P5(int i));

View File

@ -1,29 +1,27 @@
DeconstructionPatternErrors.java:15:28: compiler.err.underscore.as.identifier
DeconstructionPatternErrors.java:15:29: compiler.err.expected: token.identifier
DeconstructionPatternErrors.java:43:37: compiler.err.illegal.start.of.type
DeconstructionPatternErrors.java:45:28: compiler.err.illegal.start.of.type
DeconstructionPatternErrors.java:47:42: compiler.err.expected: ';'
DeconstructionPatternErrors.java:47:43: compiler.err.not.stmt
DeconstructionPatternErrors.java:16:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List<java.lang.String>, java.util.ArrayList<java.lang.Integer>)
DeconstructionPatternErrors.java:17:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList<java.lang.Integer>
DeconstructionPatternErrors.java:18:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
DeconstructionPatternErrors.java:19:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.String)
DeconstructionPatternErrors.java:20:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, DeconstructionPatternErrors.P)
DeconstructionPatternErrors.java:42:37: compiler.err.illegal.start.of.type
DeconstructionPatternErrors.java:44:28: compiler.err.illegal.start.of.type
DeconstructionPatternErrors.java:46:42: compiler.err.expected: ';'
DeconstructionPatternErrors.java:46:43: compiler.err.not.stmt
DeconstructionPatternErrors.java:15:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List<java.lang.String>, java.util.ArrayList<java.lang.Integer>)
DeconstructionPatternErrors.java:16:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList<java.lang.Integer>
DeconstructionPatternErrors.java:17:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
DeconstructionPatternErrors.java:18:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.String)
DeconstructionPatternErrors.java:19:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, DeconstructionPatternErrors.P)
DeconstructionPatternErrors.java:20:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable
DeconstructionPatternErrors.java:21:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable
DeconstructionPatternErrors.java:22:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable
DeconstructionPatternErrors.java:23:26: compiler.err.incorrect.number.of.nested.patterns: int, int,compiler.misc.type.none
DeconstructionPatternErrors.java:24:26: compiler.err.incorrect.number.of.nested.patterns: int, int,int
DeconstructionPatternErrors.java:25:36: compiler.err.cant.resolve.location: kindname.class, Unresolvable, , , (compiler.misc.location: kindname.class, DeconstructionPatternErrors, null)
DeconstructionPatternErrors.java:25:26: compiler.err.incorrect.number.of.nested.patterns: int, int,Unresolvable
DeconstructionPatternErrors.java:26:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord<java.lang.String>
DeconstructionPatternErrors.java:27:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord<java.lang.String>
DeconstructionPatternErrors.java:28:44: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
DeconstructionPatternErrors.java:28:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord<java.lang.String>
DeconstructionPatternErrors.java:29:40: compiler.err.match.binding.exists
DeconstructionPatternErrors.java:30:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, meth()
DeconstructionPatternErrors.java:30:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, meth()
DeconstructionPatternErrors.java:31:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte)
DeconstructionPatternErrors.java:32:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
DeconstructionPatternErrors.java:34:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte)
DeconstructionPatternErrors.java:35:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
28 errors
DeconstructionPatternErrors.java:22:26: compiler.err.incorrect.number.of.nested.patterns: int, int,compiler.misc.type.none
DeconstructionPatternErrors.java:23:26: compiler.err.incorrect.number.of.nested.patterns: int, int,int
DeconstructionPatternErrors.java:24:36: compiler.err.cant.resolve.location: kindname.class, Unresolvable, , , (compiler.misc.location: kindname.class, DeconstructionPatternErrors, null)
DeconstructionPatternErrors.java:24:26: compiler.err.incorrect.number.of.nested.patterns: int, int,Unresolvable
DeconstructionPatternErrors.java:25:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord<java.lang.String>
DeconstructionPatternErrors.java:26:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord<java.lang.String>
DeconstructionPatternErrors.java:27:44: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
DeconstructionPatternErrors.java:27:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord<java.lang.String>
DeconstructionPatternErrors.java:28:40: compiler.err.match.binding.exists
DeconstructionPatternErrors.java:29:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, meth()
DeconstructionPatternErrors.java:29:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, meth()
DeconstructionPatternErrors.java:30:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte)
DeconstructionPatternErrors.java:31:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
DeconstructionPatternErrors.java:33:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte)
DeconstructionPatternErrors.java:34:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
26 errors

View File

@ -23,6 +23,7 @@
/*
* @test
* @enablePreview
* @summary Test behavior of Pretty
* @modules jdk.compiler
*/
@ -48,38 +49,46 @@ public class PrettyTest {
String code = "class Test {\n" +
" boolean t(Object o) {\n" +
" boolean b;\n" +
" boolean _ = true;\n" +
" b = o instanceof String s;\n" +
" b = o instanceof R(String s);\n" +
" b = o instanceof R(var s);\n" +
" b = o instanceof R2(R(var s), String t);\n" +
" b = o instanceof R2(R(var s), var t);\n" +
" b = o instanceof R(String _);\n" +
" b = o instanceof R2(R(var _), var _);\n" +
" b = o instanceof R2(R(_), var t);\n" +
" }\n" +
" record R(String s) {}\n" +
" record R2(R r, String s) {}\n" +
"}\n";
String pretty = parse(code).toString().replaceAll("\\R", "\n");
String expected = """
\n\
class Test {
\n\
boolean t(Object o) {
boolean b;
b = o instanceof String s;
b = o instanceof R(String s);
b = o instanceof R(/*missing*/ s);
b = o instanceof R2(R(/*missing*/ s), String t);
b = o instanceof R2(R(/*missing*/ s), /*missing*/ t);
}
\n\
class R {
private final String s;
}
\n\
class R2 {
private final R r;
private final String s;
}
}""";
\n\
class Test {
\n\
boolean t(Object o) {
boolean b;
boolean _ = true;
b = o instanceof String s;
b = o instanceof R(String s);
b = o instanceof R(/*missing*/ s);
b = o instanceof R2(R(/*missing*/ s), String t);
b = o instanceof R2(R(/*missing*/ s), /*missing*/ t);
b = o instanceof R(String _);
b = o instanceof R2(R(/*missing*/ _), /*missing*/ _);
b = o instanceof R2(R(_), /*missing*/ t);
}
\n\
class R {
private final String s;
}
\n\
class R2 {
private final R r;
private final String s;
}
}""";
if (!expected.equals(pretty)) {
throw new AssertionError("Actual prettified source: " + pretty);
}

View File

@ -0,0 +1,306 @@
/*
* Copyright (c) 2023, 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
* @bug 8304246
* @summary Compiler Implementation for Unnamed patterns and variables
* @enablePreview
* @compile Unnamed.java
* @run main Unnamed
*/
import java.util.Objects;
public class Unnamed {
public static void main(String[] args) throws Throwable {
new Unnamed().run();
}
public void run() {
assertEquals(1, testMultiValuesTopLevel(new R1()));
assertEquals(2, testMultiValuesTopLevel(new R3()));
assertEquals(1, testMultiValuesTopLevel2(new R1()));
assertEquals(2, testMultiValuesTopLevel2(new R2()));
assertEquals(2, testMultiValuesTopLevel2(new R4()));
assertEquals(1, testMultiValuesNested(new Box<>(new R1())));
assertEquals(1, testMultiValuesNested(new Box<>(new R2())));
assertEquals(2, testMultiValuesNested(new Box<>(new R3())));
assertEquals(3, testMultiValuesNested(new Box<>(new R4())));
assertEquals(1, testMultiValuesNestedUnnamedVarAndPattern(new Box<>(new R1())));
assertEquals(2, testMultiValuesNestedUnnamedVarAndPattern(new Box<>(new R4())));
assertEquals(1, testMultiValuesNestedMix(new Box<>(new R1())));
assertEquals(1, testMultiValuesNestedMix(new Box2<>(new R1())));
assertEquals(1, testMultiValuesNestedMix2(new Box<>(new R1())));
assertEquals(1, testMultiValuesNestedMix2("BOX"));
assertEquals(2, testMultiValuesNestedMix2(new Box2<>(new R1())));
assertEquals(1, testMultiValuesStatementBlock(42));
assertEquals(1, testMultiValuesStatementBlock(42.0f));
assertEquals(2, testMultiValuesStatementBlock("BOX"));
assertEquals(1, testMultiValuesStatementBlock2(new Box<>(new R1())));
assertEquals(1, testMultiValuesStatementBlock2("BOX"));
assertEquals(2, testMultiValuesStatementBlock2(new Box2<>(new R1())));
assertEquals(2, testMultiValuesGuards(new R3(), 1));
assertEquals(3, testMultiValuesGuards(new R4(), 42));
assertEquals(3, testMultiValuesGuards(new R3(), 42));
assertEquals(1, testMultiValuesNestedGuards(new Box(new R2()), 42));
assertEquals(2, testMultiValuesNestedGuards(new Box(new R3()), 1));
assertEquals(1, testMixUnconditionalAndConditional(new R1()));
assertEquals(2, testMixUnconditionalAndConditional(new R2()));
assertEquals(2, testMixUnconditionalAndConditional(new R3()));
assertEquals(1, testMultipleExpr(new Box<>(new R1())));
assertEquals(1, testUnrolledExpr(new Box<>(new R1())));
assertEquals(1, testMultipleStat(new Box<>(new R1())));
assertEquals(1, testUnrolledStat(new Box<>(new R1())));
assertEquals(2, testMixVarWithExplicit(new Box<>(new R2())));
assertEquals("binding", unnamedGuardAddsBindings("match1", "binding"));
assertEquals("any", unnamedGuardAddsBindings(42, 42));
unnamedTest();
}
private void unnamedTest() {
int _ = 0;
int _ = 1;
try (Lock _ = null) {
try (Lock _ = null) {
} catch (Exception _) {
try {
} catch (Exception _) {}
}
}
String[] strs = new String[] { "str1", "str2" };
for (var _ : strs) {
for (var _ : strs) {
}
}
TwoParams p1 = (_, _) -> {};
TwoParams p2 = (var _, var _) -> {};
TwoIntParams p3 = (int _, int b) -> {};
TwoIntParams p4 = (int _, int _) -> {};
TwoIntParamsIntRet p5 = (int _, int _) -> { return 1; };
p1.run(1, 2);
p2.run(1, 2);
p3.run(1, 2);
p4.run(1, 2);
p5.run(1, 2);
R r = new R(null);
if (r instanceof R _) {}
if (r instanceof R(_)) {}
for (int _ = 0, _ = 1, x = 1; x <= 1 ; x++) {}
}
int testMultiValuesTopLevel(Object o) {
return switch (o) {
case R1 _, R2 _ -> 1;
default -> 2;
};
}
int testMultiValuesTopLevel2(Base o) {
return switch (o) {
case R1 r -> 1;
case R2 _, R3 _, R4 _ -> 2;
};
}
int testMultiValuesNested(Box<?> b) {
return switch (b) {
case Box(R1 _), Box(R2 _) -> 1;
case Box(R3 _) -> 2;
case Box(_) -> 3;
};
}
int testMultiValuesNestedUnnamedVarAndPattern(Box<?> b) {
return switch (b) {
case Box(R1 _), Box(R2 _) -> 1;
case Box(R3 _), Box(_) -> 2;
};
}
int testMultiValuesNestedMix(Object b) {
return switch (b) {
case Box(_), Box2(_) -> 1;
default -> 2;
};
}
int testMultiValuesNestedMix2(Object b) {
return switch (b) {
case Box(_), String _ -> 1;
default -> 2;
};
}
int testMultiValuesStatementBlock(Object o) {
switch (o) {
case Integer _:
case Number _:
return 1;
default:
return 2;
}
}
int testMultiValuesStatementBlock2(Object o) {
switch (o) {
case Box(_):
case String _:
return 1;
default:
return 2;
}
}
int testMultiValuesGuards(Base b, int x) {
return switch (b) {
case R1 r -> 1;
case R2 _, R3 _, R4 _ when x == 1 -> 2;
case R2 _, R3 _, R4 _ -> 3;
};
}
int testMultiValuesNestedGuards(Box<?> b, int x) {
return switch (b) {
case Box(R1 _), Box(R2 _) -> 1;
case Box(R3 _), Box(_) when x == 1 -> 2;
case Box(_) -> 3;
};
}
int testMixUnconditionalAndConditional(Base t) {
return switch(t) {
case R1 _ -> 1;
case R2 _, Base _-> 2;
};
}
int testMultipleExpr(Box<?> t) {
return switch(t) {
case Box(R1 _), Box(R2 _) -> 1;
default -> -2;
};
}
int testUnrolledExpr(Box<?> t) {
return switch(t) {
case Box(R1 _) -> 1;
case Box(R2 _) -> 0;
default -> -2;
};
}
int testMultipleStat(Box<?> t) {
int ret = -1;
switch(t) {
case Box(R1 _), Box(R2 _):
ret = 1;
break;
default:
ret = -2;
}
return ret;
}
int testUnrolledStat(Box<?> t) {
int ret = -1;
switch(t) {
case Box(R1 _):
ret = 1;
break;
case Box(R2 _):
ret = 0;
break;
default:
ret = -2;
}
return ret;
}
int testMixVarWithExplicit(Box<?> t) {
int success = -1;
success = switch(t) {
case Box(R1 _) : {
yield 1;
}
case Box(R2 _), Box(var _) : {
yield 2;
}
default : {
yield -2;
}
};
return success;
}
String unnamedGuardAddsBindings(Object o1, Object o2) {
return switch (o1) {
case String _, Object _ when o2 instanceof String s: yield s;
case Object _: yield "any";
};
}
// JEP 443 examples
record Point(int x, int y) { }
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) { }
void jep443examples(ColoredPoint r) {
if (r instanceof ColoredPoint(Point(int x, int y), _)) { }
if (r instanceof ColoredPoint(_, Color c)) { }
if (r instanceof ColoredPoint(Point(int x, _), _)) { }
if (r instanceof ColoredPoint(Point(int x, int _), Color _)) { }
if (r instanceof ColoredPoint _) { }
}
class Lock implements AutoCloseable {
@Override
public void close() {}
}
interface TwoParams {
public void run(Object o1, Object o2);
}
interface TwoIntParams {
public void run(int o1, int o2);
}
interface TwoIntParamsIntRet {
public int run(int a, int b);
}
record R(Object o) {}
sealed abstract class Base permits R1, R2, R3, R4 { }
final class R1 extends Base { }
final class R2 extends Base { }
final class R3 extends Base { }
final class R4 extends Base { }
record Box<T extends Base>(T content) { }
record Box2<T extends Base>(T content) { }
void assertEquals(Object expected, Object actual) {
if (!Objects.equals(expected, actual)) {
throw new AssertionError("Expected: " + expected + ", but got: " + actual);
}
}
}

View File

@ -0,0 +1,118 @@
/**
* @test /nodynamiccopyright/
* @bug 8304246
* @summary Compiler Implementation for Unnamed patterns and variables
* @enablePreview
* @compile/fail/ref=UnnamedErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW UnnamedErrors.java
*/
public class UnnamedErrors {
private int _; // error
private int _, x; // error
private int x, _, y, _, z, _; // error
private int _ = 0, _ = 1; // error
private int a = 0, _ = 1; // error
record R(int _) {} //no record components
UnnamedErrors(int _) {} //no constructor parameters
void test(int _) {} //no method parameters
record RR(int x) {}
void test2() {
Object o = Integer.valueOf(42);
if (o instanceof _) {} //no top level
if (o instanceof _(int x)) {} //no record pattern head
switch (o) {
case _:
System.out.println("no underscore top level");
default:
System.out.println("");
}
switch (o) {
case var _:
System.out.println("no var _ top level");
default:
System.out.println("");
}
}
void dominanceError(Object o) {
switch (o) {
case Number _ ->
System.out.println("A Number");
case Integer _, String _ -> // Error - dominated case pattern: `Integer _`
System.out.println("An Integer or a String");
default ->
System.out.println("rest");
}
}
void mixedNamedUnnamedError(Object o) {
switch (o) {
case Integer i, String _ ->
System.out.println("named/unnamed");
default ->
System.out.println("rest");
}
switch (o) {
case Integer _, String s ->
System.out.println("unnamed/named");
default ->
System.out.println("rest");
}
switch (o) {
case PairIS(_, _), String s ->
System.out.println("unnamed patterns/named");
default ->
System.out.println("rest");
}
}
private void test1() {
try (Lock _ = null) {
} catch (_) { }
}
int guardErrors(Object o, int x1, int x2) {
return switch (o) {
case Integer _ when x1 == 2, String _ when x2 == 1 -> 1;
default -> 2;
};
}
int testMixVarWithExplicitDominanceError(Box<?> t) {
int success = -1;
success = switch(t) {
case Box(var _), Box(R2 _) : {
yield 1;
}
default : {
yield -2;
}
};
return success;
}
void testUnderscoreWithoutInitializer() {
int _;
int x1 = 1, _, x2;
for (int x = 1, _; x<=1; x++) {
}
}
class Lock implements AutoCloseable {
@Override
public void close() {}
}
record PairIS(int i, String s) {}
sealed abstract class Base permits R1, R2 { }
final class R1 extends Base { }
final class R2 extends Base { }
record Box<T extends Base>(T content) { }
}

View File

@ -0,0 +1,35 @@
UnnamedErrors.java:9:17: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:10:17: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:11:20: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:11:26: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:11:32: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:12:17: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:12:24: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:13:24: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:15:18: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:16:23: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:17:19: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:22:26: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:24:26: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:27:18: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:34:18: compiler.err.restricted.type.not.allowed.here: var
UnnamedErrors.java:77:18: compiler.err.use.of.underscore.not.allowed
UnnamedErrors.java:77:19: compiler.err.expected: token.identifier
UnnamedErrors.java:82:40: compiler.err.expected2: :, ->
UnnamedErrors.java:82:51: compiler.err.expected: =
UnnamedErrors.java:82:58: compiler.err.expected: ';'
UnnamedErrors.java:101:14: compiler.err.expected: =
UnnamedErrors.java:102:22: compiler.err.expected: =
UnnamedErrors.java:104:26: compiler.err.expected: =
UnnamedErrors.java:11:17: compiler.err.already.defined: kindname.variable, x, kindname.class, UnnamedErrors
UnnamedErrors.java:36:13: compiler.err.unconditional.pattern.and.default
UnnamedErrors.java:45:18: compiler.err.pattern.dominated
UnnamedErrors.java:54:29: compiler.err.flows.through.from.pattern
UnnamedErrors.java:61:29: compiler.err.flows.through.from.pattern
UnnamedErrors.java:68:32: compiler.err.flows.through.from.pattern
UnnamedErrors.java:82:56: compiler.err.already.defined: kindname.variable, x2, kindname.method, guardErrors(java.lang.Object,int,int)
UnnamedErrors.java:83:13: compiler.err.switch.mixing.case.types
UnnamedErrors.java:90:30: compiler.err.pattern.dominated
- compiler.note.preview.filename: UnnamedErrors.java, DEFAULT
- compiler.note.preview.recompile
32 errors

View File

@ -35,13 +35,7 @@
import java.util.Set;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import static javax.lang.model.SourceVersion.*;
import javax.lang.model.element.*;
import javax.lang.model.util.*;
import static javax.lang.model.util.ElementFilter.*;
import static javax.tools.Diagnostic.Kind.*;
import static javax.tools.StandardLocation.*;
import java.io.*;

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2023, 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
* @bug 8302344 8307007
* @summary Compiler Implementation for Unnamed patterns and variables
* @library /tools/javac/lib
* @modules jdk.compiler
* @build JavacTestingAbstractProcessor
* @enablePreview
* @compile TestUnnamedVariableElement.java
* @compile --enable-preview -source ${jdk.version} -processor TestUnnamedVariableElement -proc:only TestUnnamedVariableElementData.java
*/
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.lang.model.util.Elements;
import java.util.*;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import java.io.StringWriter;
public class TestUnnamedVariableElement extends JavacTestingAbstractProcessor implements AutoCloseable {
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
if (!roundEnv.processingOver()) {
Trees trees = Trees.instance(processingEnv);
for(Element rootElement : roundEnv.getRootElements()) {
TreePath treePath = trees.getPath(rootElement);
(new UnnamedVariableScanner(processingEnv.getElementUtils(), trees)).
scan(treePath.getCompilationUnit(), null);
}
}
return true;
}
@Override
public void close() {}
class UnnamedVariableScanner extends TreePathScanner<Void, Void> {
private final Elements elements;
private Trees trees;
public UnnamedVariableScanner(Elements elements, Trees trees) {
super();
this.elements = elements;
this.trees = trees;
}
@Override
public Void visitVariable(VariableTree node, Void unused) {
handleTreeAsLocalVar(getCurrentPath());
if(!node.getName().isEmpty()) {
throw new RuntimeException("Expected empty name as the name of the Tree API but got: " + node.getName());
}
return super.visitVariable(node, unused);
}
private void handleTreeAsLocalVar(TreePath tp) {
VariableElement element = (VariableElement) trees.getElement(tp);
System.out.println("Name: " + element.getSimpleName() +
"\tKind: " + element.getKind());
if (element.getKind() != ElementKind.LOCAL_VARIABLE) {
throw new RuntimeException("Expected a local variable, but got: " +
element.getKind());
}
StringWriter out = new StringWriter();
String expected = "int _;";
elements.printElements(out, element);
if (!expected.equals(out.toString().trim())) {
throw new RuntimeException("Expected: " + expected + ", but got: " + out.toString());
}
testUnnamedVariable(element);
}
}
/**
* Verify that a local variable modeled as an element behaves
* as expected under 6 and latest specific visitors.
*/
private static void testUnnamedVariable(Element element) {
ElementKindVisitor visitorLatest =
new ElementKindVisitor<Object, Void>() {
@Override
public Object visitVariableAsLocalVariable(VariableElement e,
Void p) {
return e;
}
};
if (visitorLatest.visit(element) == null) {
throw new RuntimeException("Null result of a resource variable visitation.");
}
}
}

View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2023, 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
* @bug 8302344 8307007
* @summary Compiler Implementation for Unnamed patterns and variables
* @library /tools/javac/lib
* @modules jdk.compiler
* @build JavacTestingAbstractProcessor
* @compile TestUnnamedVariableElement8.java
* @compile -source 8 -processor TestUnnamedVariableElement8 -proc:only TestUnnamedVariableElementData.java
*/
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import java.util.Set;
public class TestUnnamedVariableElement8 extends JavacTestingAbstractProcessor implements AutoCloseable {
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
if (!roundEnv.processingOver()) {
Trees trees = Trees.instance(processingEnv);
for(Element rootElement : roundEnv.getRootElements()) {
TreePath treePath = trees.getPath(rootElement);
(new UnnamedVariableScanner(trees)).
scan(treePath.getCompilationUnit(), null);
}
}
return true;
}
@Override
public void close() {}
class UnnamedVariableScanner extends TreePathScanner<Void, Void> {
private Trees trees;
public UnnamedVariableScanner(Trees trees) {
super();
this.trees = trees;
}
@Override
public Void visitVariable(VariableTree node, Void unused) {
handleTreeAsLocalVar(getCurrentPath());
return super.visitVariable(node, unused);
}
private void handleTreeAsLocalVar(TreePath tp) {
Element element = trees.getElement(tp);
System.out.println("Name: " + element.getSimpleName() +
"\tKind: " + element.getKind());
if (element.getKind() != ElementKind.LOCAL_VARIABLE) {
throw new RuntimeException("Expected a local variable, but got: " +
element.getKind());
}
if (!element.getSimpleName().toString().equals("_")) {
throw new RuntimeException("Expected _ for simple name of an unnamed variable, but got: " +
element.getSimpleName());
}
testUnnamedVariable(element);
}
}
/**
* Verify that a local variable modeled as an element behaves
* as expected under 6 and latest specific visitors.
*/
private static void testUnnamedVariable(Element element) {
ElementKindVisitor visitorLatest =
new ElementKindVisitor<Object, Void>() {
@Override
public Object visitVariableAsLocalVariable(VariableElement e,
Void p) {
return e;
}
};
if (visitorLatest.visit(element) == null) {
throw new RuntimeException("Null result of a resource variable visitation.");
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2023, 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.
*/
public class TestUnnamedVariableElementData {
private void test() {
int _ = 0;
}
}

View File

@ -67,8 +67,6 @@ public class VarTree {
"java.lang.String testVar");
test.run("java.util.function.Consumer<String> c = (|var testVar|) -> {};",
"java.lang.String testVar");
test.run("java.util.function.IntBinaryOperator c = (var x, |testType|) -> 1;",
"testType ");
}
void run(String code, String expected) throws IOException {