final fields cannot be modified inside its class

This commit is contained in:
Max Medvedev
2013-05-13 16:16:55 +04:00
parent 8746866625
commit 6a6f85266d
4 changed files with 83 additions and 4 deletions
@@ -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}''
@@ -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;
@@ -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) {
@@ -1368,5 +1368,48 @@ def bar
''')
}
void testFinalFieldRewrite() {
testHighlighting('''\
class A {
final foo = 1
def A() {
foo = 2 //no error
}
def foo() {
<error descr="Cannot assign a value to final field 'foo'">foo</error> = 2
}
}
new A().foo = 2 //no error
''')
}
void testStaticFinalFieldRewrite() {
testHighlighting('''\
class A {
static final foo = 1
def A() {
<error descr="Cannot assign a value to final field 'foo'">foo</error> = 2
}
static {
foo = 2 //no error
}
def foo() {
<error descr="Cannot assign a value to final field 'foo'">foo</error> = 2
}
static def bar() {
<error descr="Cannot assign a value to final field 'foo'">foo</error> = 2
}
}
A.foo = 3 //no error
''')
}
}