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