GROOVY-11372: STC: read-only property via extension method
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 2fbc6b9..e846fae 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1720,7 +1720,6 @@
}
// GROOVY-5568, GROOVY-9115, GROOVY-9123: the property may be defined by an extension
- if (readMode) // GROOVY-11372
for (ClassNode dgmReceiver : isPrimitiveType(receiverType) ? new ClassNode[]{receiverType, getWrapper(receiverType)} : new ClassNode[]{receiverType}) {
Set<MethodNode> methods = findDGMMethodsForClassNode(loader, dgmReceiver, getterName);
for (MethodNode method : findDGMMethodsForClassNode(loader, dgmReceiver, isserName)) {
@@ -1735,9 +1734,9 @@
!typeCheckMethodsWithGenerics(dgmReceiver, ClassNode.EMPTY_ARRAY, method)
);
}
- if (!methods.isEmpty()) {
- List<MethodNode> bestMethods = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY);
- if (bestMethods.size() == 1) {
+ List<MethodNode> bestMethods = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY);
+ if (bestMethods.size() == 1) {
+ if (readMode) {
MethodNode getter = bestMethods.get(0);
if (visitor != null) {
visitor.visitMethod(getter);
@@ -1746,6 +1745,8 @@
storeInferredTypeForPropertyExpression(pexp, returnType);
storeTargetMethod(pexp, getter);
return true;
+ } else if (setters.isEmpty()) { // GROOVY-11372
+ pexp.putNodeMetaData(READONLY_PROPERTY, Boolean.TRUE);
}
}
}
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index 0b92a41..530f72c 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -692,7 +692,7 @@
'''
}
- // GROOVY-11369
+ // GROOVY-11369, GROOVY-11372
void testMapPropertyAccess5() {
assertScript '''
def map = [:]
@@ -706,14 +706,17 @@
map.empty = null // not read-only property
map.class = null // not read-only property
map.metaClass = null // not read-only property
- map.properties = null
assert map.containsKey('entry')
assert map.containsKey('empty')
assert map.containsKey('class')
assert !map.containsKey('metaClass')
- assert map.containsKey('properties')
'''
+ shouldFailWithMessages '''
+ def map = [:]
+ map.properties = null
+ ''',
+ 'Cannot set read-only property: properties'
}
// GROOVY-8074
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
index 5ff4102..e66dd10 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
@@ -912,6 +912,12 @@
assert map.metaClass != null
assert !map.containsKey('metaClass')
'''
+
+ shouldFailWithMessages '''
+ def map = [:]
+ map.properties = null
+ ''',
+ 'Cannot set read-only property: properties'
}
// GROOVY-11367, GROOVY-11368