[groovy] IDEA-207415: Highlight field initializers with disabled `defaults`
authorKonstantin Nisht <konstantin.nisht@jetbrains.com>
Fri, 7 Aug 2020 15:07:12 +0000 (18:07 +0300)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Mon, 10 Aug 2020 13:53:54 +0000 (13:53 +0000)
GitOrigin-RevId: 3971abe5c224daf518756424fe2486f8567f3d86

plugins/groovy/groovy-psi/resources/messages/GroovyBundle.properties
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/GeneratedConstructorCollector.kt
plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TupleConstructorTest.groovy

index 652940d26de4441e26947078d93026c3d527db65..58563db0e97fe869a1176e240c83773b354cf94f 100644 (file)
@@ -211,6 +211,7 @@ field.should.be.immutable=Field ''{0}'' should have immutable type or be declare
 immutable.options.property.not.exist=Property ''{0}'' does not exist
 explicit.includes.and.excludes=Only one of \'includes\' and \'excludes\' should be supplied
 require.closure.as.attribute.value=Expected closure as attribute value
+initializers.are.forbidden.with.defaults=Default values are forbidden with \'defaults\' disabled in @TupleConstructor
 
 #Groovy 3.0 checks
 illegal.default.modifier=Modifier 'default' makes sense only in interface's methods
index 8f3bbc03465d28421ee66972e04f887ab0220305..119f795370b4d8c258aa55b2e7cbf30a4102049d 100644 (file)
@@ -78,6 +78,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatem
 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition;
 import org.jetbrains.plugins.groovy.lang.psi.api.types.*;
 import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper;
+import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil;
 import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
 import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.modifiers.GrAnnotationCollector;
 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
@@ -86,6 +87,7 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
 import org.jetbrains.plugins.groovy.lang.psi.util.*;
 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
 import org.jetbrains.plugins.groovy.lang.resolve.api.GroovyConstructorReference;
+import org.jetbrains.plugins.groovy.lang.resolve.ast.GeneratedConstructorCollector;
 import org.jetbrains.plugins.groovy.lang.resolve.ast.InheritConstructorContributor;
 import org.jetbrains.plugins.groovy.transformations.immutable.GrImmutableUtils;
 
@@ -577,6 +579,24 @@ public class GroovyAnnotator extends GroovyElementVisitor {
     if (field.getTypeElementGroovy() == null && field.getContainingClass() instanceof GrAnnotationTypeDefinition) {
       myHolder.newAnnotation(HighlightSeverity.ERROR, GroovyBundle.message("annotation.field.should.have.type.declaration")).range(field.getNameIdentifierGroovy()).create();
     }
+    checkInitializer(field);
+  }
+
+  private void checkInitializer(@NotNull GrField field) {
+    PsiExpression initializer = field.getInitializer();
+    if (initializer == null) return;
+    PsiClass containingClass = field.getContainingClass();
+    if (containingClass == null) return;
+    PsiAnnotation tupleConstructor = containingClass.getAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR);
+    if (tupleConstructor == null) return;
+    if (!Boolean.FALSE.equals(GrAnnotationUtil.inferBooleanAttribute(tupleConstructor, "defaults"))) return;
+    List<String> excludes = GeneratedConstructorCollector.getIdentifierList(tupleConstructor, "excludes");
+    List<String> includes = GeneratedConstructorCollector.getIdentifierList(tupleConstructor, "includes");
+    if ((excludes != null && !excludes.contains(field.getName())) || (includes != null && includes.contains(field.getName()))) {
+      myHolder.newAnnotation(HighlightSeverity.ERROR, GroovyBundle.message("initializers.are.forbidden.with.defaults"))
+        .range(initializer)
+        .create();
+    }
   }
 
   @Override
index f4d833ca134d6601c69cb4c5e45aab67374fc3c5..dff960d394859860298e8006221d521ad78ab327 100644 (file)
@@ -97,6 +97,7 @@ class GeneratedConstructorCollector(tupleConstructor: PsiAnnotation?,
 
     private fun String.isInternal(): Boolean = contains("$")
 
+    @JvmStatic
     fun getIdentifierList(annotation: PsiAnnotation, attributeName: String): List<String>? {
       annotation.takeIf { it.hasAttribute(attributeName) } ?: return null
       val rawIdentifiers = GrAnnotationUtil.inferStringAttribute(annotation, attributeName)
index 667b392cb8e3b2a66ac77ed85189dd719823a065..247f168e40850e62cdbf9547211049df0f36ae5f 100644 (file)
@@ -154,7 +154,6 @@ static void main(String[] args) {
     highlightingTest """
 @groovy.transform.TupleConstructor(defaults = false)
 class Rr {
-// todo: defval
     String actionType
     long referrerCode;
     boolean referrerUrl;
@@ -281,6 +280,18 @@ class Rr {}
 """
   }
 
+  @Test
+  void 'forbidden initializer'() {
+    highlightingTest """
+@groovy.transform.TupleConstructor(defaults = false, includes = ['a', 'b'])
+class Rr {
+  String a = <error>""</error>
+  int b = <error>1000</error>
+  boolean c = true
+}
+"""
+  }
+
   @Test
   void 'test visibility options'() {
     fixture.addFileToProject 'other.groovy', """