[groovy] IDEA-207415: Add check against two `callSuper` and super in `pre`
authorKonstantin Nisht <konstantin.nisht@jetbrains.com>
Tue, 11 Aug 2020 16:22:10 +0000 (19:22 +0300)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Thu, 13 Aug 2020 00:36:29 +0000 (00:36 +0000)
GitOrigin-RevId: f9ec3e5116da546f61749a1bc2f313001ae6b764

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/annotator/checkers/TupleConstructorAnnotationChecker.kt
plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TupleConstructorTest.groovy

index 58563db0e97fe869a1176e240c83773b354cf94f..4e8715e5eef986446e49958250b31738df33db33 100644 (file)
@@ -212,6 +212,7 @@ 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
+super.is.not.allowed.in.pre.with.call.super=Call to super is not allowed in \'pre\' with \'callSuper\' enabled
 
 #Groovy 3.0 checks
 illegal.default.modifier=Modifier 'default' makes sense only in interface's methods
index 01a671b6f58b3ca9f35c00d631f7ed29408aedff..3073a39fdcd4af330370f3002704f9e8785ca20d 100644 (file)
@@ -1,6 +1,7 @@
 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.plugins.groovy.annotator;
 
+import com.intellij.codeInsight.AnnotationUtil;
 import com.intellij.codeInsight.ClassUtil;
 import com.intellij.codeInsight.generation.OverrideImplementExploreUtil;
 import com.intellij.codeInsight.intention.IntentionAction;
@@ -39,6 +40,7 @@ import org.jetbrains.plugins.groovy.GroovyBundle;
 import org.jetbrains.plugins.groovy.GroovyLanguage;
 import org.jetbrains.plugins.groovy.annotator.checkers.AnnotationChecker;
 import org.jetbrains.plugins.groovy.annotator.checkers.CustomAnnotationChecker;
+import org.jetbrains.plugins.groovy.annotator.checkers.TupleConstructorAnnotationChecker;
 import org.jetbrains.plugins.groovy.annotator.intentions.*;
 import org.jetbrains.plugins.groovy.codeInspection.bugs.GrModifierFix;
 import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
@@ -89,7 +91,6 @@ 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.lang.resolve.ast.contributor.SyntheticKeywordConstructorContributor;
 import org.jetbrains.plugins.groovy.transformations.immutable.GrImmutableUtils;
 
 import java.util.*;
@@ -487,15 +488,13 @@ public class GroovyAnnotator extends GroovyElementVisitor {
     }
 
     PsiAnnotation anno = typeDefinition.getAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR);
-    if (anno == null) return;
-    GrClosableBlock block = GrAnnotationUtil.inferClosureAttribute(anno, "pre");
-    if (block == null) return;
-    GrStatement[] statements = block.getStatements();
-    if (!(statements.length != 0 &&
-          statements[0] instanceof GrMethodCall &&
-          SyntheticKeywordConstructorContributor.isSyntheticConstructorCall((GrMethodCall)statements[0]))) {
-      holder.newAnnotation(HighlightSeverity.ERROR, GroovyBundle.message("there.is.no.default.constructor.available.in.class.0", qName))
-        .range(block).create();
+    if (anno != null) {
+      if (!TupleConstructorAnnotationChecker.isSuperCalledInPre(anno)) {
+        PsiNameValuePair preAttribute = AnnotationUtil.findDeclaredAttribute(anno, "pre");
+        Objects.requireNonNull(preAttribute);
+        holder.newAnnotation(HighlightSeverity.ERROR, GroovyBundle.message("there.is.no.default.constructor.available.in.class.0", qName))
+          .range(preAttribute).create();
+      }
     }
   }
 
index 3c8589b84042164450ae7c170f8ea63d4642a209..81ee550678d48c2c0c0ab9596a7e0d4a46df259b 100644 (file)
@@ -4,16 +4,26 @@ package org.jetbrains.plugins.groovy.annotator.checkers
 import com.intellij.codeInsight.AnnotationUtil
 import com.intellij.lang.annotation.AnnotationHolder
 import com.intellij.lang.annotation.HighlightSeverity
+import com.intellij.psi.PsiAnnotation
 import com.intellij.psi.PsiElement
 import org.jetbrains.plugins.groovy.GroovyBundle
 import org.jetbrains.plugins.groovy.lang.psi.api.GrFunctionalExpression
 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall
+import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil
+import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil.inferClosureAttribute
 import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames
 import org.jetbrains.plugins.groovy.lang.resolve.ast.GeneratedConstructorCollector
+import org.jetbrains.plugins.groovy.lang.resolve.ast.contributor.SyntheticKeywordConstructorContributor.Companion.isSyntheticConstructorCall
 
 class TupleConstructorAnnotationChecker : CustomAnnotationChecker() {
 
   companion object {
+    @JvmStatic
+    fun isSuperCalledInPre(annotation: PsiAnnotation): Boolean =
+      (inferClosureAttribute(annotation, "pre")?.statements?.firstOrNull() as? GrMethodCall)
+        .run(::isSyntheticConstructorCall)
+
     private fun registerIdentifierListError(holder: AnnotationHolder, element: PsiElement) =
       holder.newAnnotation(HighlightSeverity.ERROR, GroovyBundle.message("explicit.includes.and.excludes"))
         .range(element)
@@ -23,6 +33,11 @@ class TupleConstructorAnnotationChecker : CustomAnnotationChecker() {
       holder.newAnnotation(HighlightSeverity.ERROR, GroovyBundle.message("require.closure.as.attribute.value"))
         .range(element)
         .create()
+
+    private fun registerDuplicateSuperError(holder: AnnotationHolder, element: PsiElement) =
+      holder.newAnnotation(HighlightSeverity.ERROR, GroovyBundle.message("super.is.not.allowed.in.pre.with.call.super"))
+        .range(element)
+        .create()
   }
 
   override fun checkArgumentList(holder: AnnotationHolder, annotation: GrAnnotation): Boolean {
@@ -43,6 +58,12 @@ class TupleConstructorAnnotationChecker : CustomAnnotationChecker() {
     if (post != null && post !is GrFunctionalExpression) {
       registerClosureError(holder, post)
     }
+    val callSuper = AnnotationUtil.findDeclaredAttribute(annotation, "callSuper")
+    if (callSuper != null && pre != null &&
+        GrAnnotationUtil.inferBooleanAttribute(annotation, "callSuper") == true &&
+        isSuperCalledInPre(annotation)) {
+      registerDuplicateSuperError(holder, callSuper)
+    }
     return false
   }
 }
\ No newline at end of file
index e07b0d2c0e26238d401643253239acf9a3a9b1b8..ac45a7a064236030312c246bed956921f990d8cd 100644 (file)
@@ -344,9 +344,6 @@ class NN { NN(String s) {} }
 @groovy.transform.CompileStatic
 @groovy.transform.TupleConstructor(pre = { super("") })
 class Rr extends NN {
-    int q
-    
-    def foo() {}
 }"""
   }
 
@@ -356,11 +353,19 @@ class Rr extends NN {
 class NN { NN(String s) {} }
 
 @groovy.transform.CompileStatic
-@groovy.transform.TupleConstructor(pre = <error>{ }</error>)
+@groovy.transform.TupleConstructor(<error>pre = { }</error>)
+class Rr extends NN {
+}"""
+  }
+
+  @Test
+  void 'pre highlighting 2'() {
+    highlightingTest """
+class NN { }
+
+@groovy.transform.CompileStatic
+@groovy.transform.TupleConstructor(pre = { super() }, <error>callSuper = true</error>)
 class Rr extends NN {
-    int q
-    
-    def foo() {}
 }"""
   }
 }