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