Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/antlr/GroovyLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ THROW : 'throw';
THROWS : 'throws';
TRANSIENT : 'transient';
TRY : 'try';
VAL : 'val';
VAR : 'var';
VOID : 'void';
VOLATILE : 'volatile';
Expand Down
6 changes: 5 additions & 1 deletion src/antlr/GroovyParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ modifier
| TRANSIENT
| VOLATILE
| DEF
| VAL
| VAR
)
;
Expand Down Expand Up @@ -174,6 +175,7 @@ variableModifier
: annotation
| m=( FINAL
| DEF
| VAL
| VAR
// Groovy supports declaring local variables as instance/class fields,
// e.g. import groovy.transform.*; @Field static List awe = [1, 2, 3]
Expand Down Expand Up @@ -698,7 +700,7 @@ enhancedForControl
;

indexVariable
: (BuiltInPrimitiveType | DEF | VAR)? identifier
: (BuiltInPrimitiveType | DEF | VAL | VAR)? identifier
;

originalForControl
Expand Down Expand Up @@ -1234,6 +1236,7 @@ identifier
| RECORD
| SEALED
| TRAIT
| VAL
| VAR
| YIELD
;
Expand Down Expand Up @@ -1289,6 +1292,7 @@ keywords
| TRAIT
| THREADSAFE
| TRY
| VAL
| VAR
| VOLATILE
| WHILE
Expand Down
14 changes: 7 additions & 7 deletions src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1122,8 +1122,8 @@ public ClassNode visitTypeDeclaration(final TypeDeclarationContext ctx) {
public ClassNode visitClassDeclaration(final ClassDeclarationContext ctx) {
String packageName = Optional.ofNullable(this.moduleNode.getPackageName()).orElse("");
String className = this.visitIdentifier(ctx.identifier());
if ("var".equals(className)) {
throw createParsingFailedException("var cannot be used for type declarations", ctx.identifier());
if ("var".equals(className) || "val".equals(className)) {
throw createParsingFailedException(className + " cannot be used for type declarations", ctx.identifier());
}

boolean isAnnotation = asBoolean(ctx.AT());
Expand Down Expand Up @@ -1644,8 +1644,8 @@ public MethodNode visitCompactConstructorDeclaration(final CompactConstructorDec
throw createParsingFailedException("Only record can have compact constructor", ctx);
}

if (new ModifierManager(this, ctx.getNodeMetaData(COMPACT_CONSTRUCTOR_DECLARATION_MODIFIERS)).containsAny(VAR)) {
throw createParsingFailedException("var cannot be used for compact constructor declaration", ctx);
if (new ModifierManager(this, ctx.getNodeMetaData(COMPACT_CONSTRUCTOR_DECLARATION_MODIFIERS)).containsAny(VAL, VAR)) {
throw createParsingFailedException("val/var cannot be used for compact constructor declaration", ctx);
}

String methodName = this.visitMethodName(ctx.methodName());
Expand Down Expand Up @@ -1682,8 +1682,8 @@ public void visitPropertyExpression(final PropertyExpression expression) {
public MethodNode visitMethodDeclaration(final MethodDeclarationContext ctx) {
ModifierManager modifierManager = createModifierManager(ctx);

if (modifierManager.containsAny(VAR)) {
throw createParsingFailedException("var cannot be used for method declarations", ctx);
if (modifierManager.containsAny(VAL, VAR)) {
throw createParsingFailedException("val/var cannot be used for method return types", ctx);
}

String methodName = this.visitMethodName(ctx.methodName());
Expand Down Expand Up @@ -4492,7 +4492,7 @@ private boolean isSyntheticPublic(final boolean isAnnotationDeclaration, final b
return true;
}

if (hasReturnType && (modifierManager.containsAny(DEF, VAR))) {
if (hasReturnType && (modifierManager.containsAny(DEF, VAL, VAR))) {
return true;
}

Expand Down
8 changes: 7 additions & 1 deletion src/main/java/org/codehaus/groovy/ast/ModifierNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import static org.apache.groovy.parser.antlr4.GroovyParser.STRICTFP;
import static org.apache.groovy.parser.antlr4.GroovyParser.SYNCHRONIZED;
import static org.apache.groovy.parser.antlr4.GroovyParser.TRANSIENT;
import static org.apache.groovy.parser.antlr4.GroovyParser.VAL;
import static org.apache.groovy.parser.antlr4.GroovyParser.VAR;
import static org.apache.groovy.parser.antlr4.GroovyParser.VOLATILE;
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
Expand All @@ -56,6 +57,7 @@ public class ModifierNode extends ASTNode {
public static final Map<Integer, Integer> MODIFIER_OPCODE_MAP = Maps.of(
ANNOTATION_TYPE, 0,
DEF, 0,
VAL, Opcodes.ACC_FINAL,
VAR, 0,

NATIVE, Opcodes.ACC_NATIVE,
Expand Down Expand Up @@ -129,7 +131,11 @@ public boolean isAnnotation() {
}

public boolean isDef() {
return Objects.equals(DEF, this.type) || Objects.equals(VAR, this.type);
return Objects.equals(DEF, this.type) || Objects.equals(VAL, this.type) || Objects.equals(VAR, this.type);
}

public boolean isVal() {
return Objects.equals(VAL, this.type);
}

public Integer getType() {
Expand Down
57 changes: 57 additions & 0 deletions src/test-resources/core/Val_01x.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

// basic val declaration
val name = "Groovy"
assert "Groovy" == name

// val as closure parameter name
[1, 2, 3].each { val ->
assert 0 < val && val < 4
}

// val as variable name (contextual keyword)
val val = "val variable name"
assert "val variable name" == val

// val in map key
def m = [val: 42]
assert m.val == 42

// val with different types
val x = 42
assert x == 42
val s = "hello"
assert s.class == String

// shallow finality - mutation OK
val list = [1, 2, 3]
list << 4
assert list == [1, 2, 3, 4]

// final val is redundant but works
final val y = 10
assert y == 10

// for loop with val
for (val i in [1, 2, 3]) { assert i > 0 }

// GString interpolation
val g = 99
assert "$g" == "99"
20 changes: 20 additions & 0 deletions src/test-resources/fail/Val_01x.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

class val {}
20 changes: 20 additions & 0 deletions src/test-resources/fail/Val_02x.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

val someMethod() {}
21 changes: 21 additions & 0 deletions src/test-resources/fail/Val_03x.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

val x = 1
x = 2
18 changes: 9 additions & 9 deletions src/test/groovy/bugs/Groovy5358.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -71,25 +71,25 @@ final class Groovy5358 {
void testSetPropertyOverrides() {
assertScript '''
class FooWorksAsMap {
def val
def stored
void setProperty(String name, value) {
val = "OK:FooWorksAsMap.$value"
stored = "OK:FooWorksAsMap.$value"
}
}
class BarWorksAsMap {
def val
def stored
}
@Category(BarWorksAsMap) class C {
void setProperty(String name, value) {
setVal("OK:BarWorksAsMap.$value")
setStored("OK:BarWorksAsMap.$value")
}
}
BarWorksAsMap.mixin C
class BazWorksAsMap {
def val
def stored
}
BazWorksAsMap.metaClass.setProperty = { String name, value ->
setVal("OK:BazWorksAsMap.$value")
setStored("OK:BazWorksAsMap.$value")
}

def objects = [
Expand All @@ -99,10 +99,10 @@ final class Groovy5358 {
[:]
]
for (def obj in objects) {
def which = "${obj.getClass().getSimpleName()}.val"
def which = "${obj.getClass().getSimpleName()}.stored"
try {
obj.val = which.startsWith('LinkedHashMap') ? "OK:LinkedHashMap.bar" : 'bar'
assert obj.val.startsWith('OK:') : "$which -> $obj.val"
obj.stored = which.startsWith('LinkedHashMap') ? "OK:LinkedHashMap.bar" : 'bar'
assert obj.stored.startsWith('OK:') : "$which -> $obj.stored"
} catch (any) {
assert false : "$which -> FAIL:$any"
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/groovy/groovy/transform/stc/LambdaTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ final class LambdaTest {
this.val = v
}
String toString() {
val as String
this.val as String
}
def <Out> Value<Out> replace(Supplier<Out> supplier) {
new Value<>(supplier.get())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,11 @@ final class GroovyParserTest {
doRunAndTestAntlr4('core/SafeChainOperator.groovy')
}

@Test
void 'groovy core - val'() {
doRunAndTestAntlr4('core/Val_01x.groovy')
}

@Test
void 'groovy core - var'() {
doRunAndTestAntlr4('core/Var_01x.groovy')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,13 @@ final class SyntaxErrorTest {
TestUtils.doRunAndShouldFail('fail/MethodCall_01x.groovy')
}

@Test
void 'groovy core - val'() {
TestUtils.doRunAndShouldFail('fail/Val_01x.groovy')
TestUtils.doRunAndShouldFail('fail/Val_02x.groovy')
TestUtils.doRunAndShouldFail('fail/Val_03x.groovy')
}

@Test
void 'groovy core - var'() {
TestUtils.doRunAndShouldFail('fail/Var_01x.groovy')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
import static org.apache.groovy.parser.antlr4.GroovyLexer.TRANSIENT;
import static org.apache.groovy.parser.antlr4.GroovyLexer.TRY;
import static org.apache.groovy.parser.antlr4.GroovyLexer.UNEXPECTED_CHAR;
import static org.apache.groovy.parser.antlr4.GroovyLexer.VAL;
import static org.apache.groovy.parser.antlr4.GroovyLexer.VAR;
import static org.apache.groovy.parser.antlr4.GroovyLexer.VOID;
import static org.apache.groovy.parser.antlr4.GroovyLexer.VOLATILE;
Expand All @@ -124,7 +125,7 @@
*/
public class SmartDocumentFilter extends DocumentFilter {
public static final List<Integer> HIGHLIGHTED_TOKEN_TYPE_LIST = Arrays.asList(AS, DEF, IN, TRAIT, THREADSAFE,
VAR, BuiltInPrimitiveType, ABSTRACT, ASSERT, BREAK, CASE, CATCH, CLASS, CONST, CONTINUE, DEFAULT, DO,
VAL, VAR, BuiltInPrimitiveType, ABSTRACT, ASSERT, BREAK, CASE, CATCH, CLASS, CONST, CONTINUE, DEFAULT, DO,
ELSE, ENUM, EXTENDS, FINAL, FINALLY, FOR, IF, GOTO, IMPLEMENTS, IMPORT, INSTANCEOF, INTERFACE,
NATIVE, NEW, NON_SEALED, NOT_IN, NOT_INSTANCEOF, PACKAGE, PERMITS, PRIVATE, PROTECTED, PUBLIC,
RECORD, RETURN, SEALED, STATIC, STRICTFP, SUPER, SWITCH, SYNCHRONIZED,
Expand Down
Loading