diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties
index 131fe230d341..2781d8faaf77 100644
--- a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties
+++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties
@@ -360,3 +360,4 @@ annotation.type.cannot.be.inner=Annotation type cannot be inner
cannot.find.operator.overload.method=Cannot resolve index access with arguments {0}
named.arguments.are.not.allowed.inside.index.operations=Named arguments are not allowed inside index operations
expected.0.to.be.inline.constant=Expected ''{0}'' to be an inline constant
+cannot.assign.a.value.to.final.field.0=Cannot assign a value to final field ''{0}''
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java
index aaa6bbf10185..ef688f9298c3 100644
--- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java
+++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java
@@ -286,6 +286,7 @@ public class GroovyAnnotator extends GroovyElementVisitor {
public void visitReferenceExpression(final GrReferenceExpression referenceExpression) {
checkStringNameIdentifier(referenceExpression);
checkThisOrSuperReferenceExpression(referenceExpression, myHolder);
+ checkFinalFieldAccess(referenceExpression);
if (ResolveUtil.isKeyOfMap(referenceExpression)) {
PsiElement nameElement = referenceExpression.getReferenceNameElement();
LOG.assertTrue(nameElement != null);
@@ -298,6 +299,40 @@ public class GroovyAnnotator extends GroovyElementVisitor {
}
}
+ private void checkFinalFieldAccess(@NotNull GrReferenceExpression ref) {
+ final PsiElement resolved = ref.resolve();
+
+ if (resolved instanceof GrField && resolved.isPhysical() && ((GrField)resolved).hasModifierProperty(FINAL) && PsiUtil.isLValue(ref)) {
+ final GrField field = (GrField)resolved;
+
+ final PsiClass containingClass = field.getContainingClass();
+ if (containingClass != null && PsiTreeUtil.isAncestor(containingClass, ref, true)) {
+ GrMember container = findClassMemberContainer(ref, containingClass);
+
+ if (field.hasModifierProperty(STATIC)) {
+ if (container instanceof GrClassInitializer && ((GrClassInitializer)container).isStatic()) {
+ return;
+ }
+ }
+ else {
+ if (container instanceof GrMethod && ((GrMethod)container).isConstructor()) {
+ return;
+ }
+ }
+
+ myHolder.createErrorAnnotation(ref, GroovyBundle.message("cannot.assign.a.value.to.final.field.0", field.getName()));
+ }
+ }
+ }
+
+ @Nullable
+ private static GrMember findClassMemberContainer(@NotNull GrReferenceExpression ref, @NotNull PsiClass aClass) {
+ for (PsiElement parent = ref.getParent(); parent != null && parent != aClass; parent = parent.getParent()) {
+ if (parent instanceof GrMember && ((GrMember)parent).getContainingClass() == aClass) return (GrMember)parent;
+ }
+ return null;
+ }
+
private void checkStringNameIdentifier(GrReferenceExpression ref) {
final PsiElement nameElement = ref.getReferenceNameElement();
if (nameElement == null) return;
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java
index f4f71ccbcef4..499c892d997c 100644
--- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java
+++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,9 +20,9 @@ import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiModifier;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
@@ -56,12 +56,12 @@ public class GrClassInitializerImpl extends GroovyPsiElementImpl implements GrCl
}
public boolean isStatic() {
- return findChildByType(GroovyTokenTypes.kSTATIC) != null;
+ return getModifierList().hasExplicitModifier(PsiModifier.STATIC);
}
public PsiClass getContainingClass() {
- PsiElement parent = getParent().getParent();
+ PsiElement parent = getParent();
if (parent instanceof GrTypeDefinitionBody) {
final PsiElement pparent = parent.getParent();
if (pparent instanceof PsiClass) {
diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy
index 10f2e8a80576..ef3b7dd3c073 100644
--- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy
+++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy
@@ -1368,5 +1368,48 @@ def bar
''')
}
+ void testFinalFieldRewrite() {
+ testHighlighting('''\
+class A {
+ final foo = 1
+
+ def A() {
+ foo = 2 //no error
+ }
+
+ def foo() {
+ foo = 2
+ }
+}
+
+new A().foo = 2 //no error
+''')
+ }
+
+ void testStaticFinalFieldRewrite() {
+ testHighlighting('''\
+class A {
+ static final foo = 1
+
+ def A() {
+ foo = 2
+ }
+
+ static {
+ foo = 2 //no error
+ }
+
+ def foo() {
+ foo = 2
+ }
+
+ static def bar() {
+ foo = 2
+ }
+}
+
+A.foo = 3 //no error
+''')
+ }
}
\ No newline at end of file