[groovy] IDEA-207415: Support @VisibilityOptions annotation
authorKonstantin Nisht <konstantin.nisht@jetbrains.com>
Fri, 7 Aug 2020 13:57:25 +0000 (16:57 +0300)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Mon, 10 Aug 2020 13:53:48 +0000 (13:53 +0000)
GitOrigin-RevId: a4b524d0c0807247a13ce86040beead94bc2085c

plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/ConstructorAnnotationsProcessor.java
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/GrVisibilityUtils.kt [new file with mode: 0644]
plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TupleConstructorTest.groovy

index cf52d9e465c30bc31955c25021f8fc51ac1fea83..68dcfa68ee017b270ffb0aac910d20e7a2a1fbb9 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2000-2018 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.
+// 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.lang.psi.util;
 
 import java.util.Set;
@@ -50,6 +50,7 @@ public interface GroovyCommonClassNames {
   String GROOVY_LANG_DELEGATES_TO = "groovy.lang.DelegatesTo";
   String GROOVY_LANG_DELEGATES_TO_TARGET = "groovy.lang.DelegatesTo.Target";
   String GROOVY_TRANSFORM_COMPILE_DYNAMIC = "groovy.transform.CompileDynamic";
+  String GROOVY_TRANSFORM_VISIBILITY_OPTIONS = "groovy.transform.VisibilityOptions";
   String GROOVY_TRANSFORM_STC_CLOSURE_PARAMS = "groovy.transform.stc.ClosureParams";
   String GROOVY_TRANSFORM_STC_SIMPLE_TYPE = "groovy.transform.stc.SimpleType";
   String GROOVY_TRANSFORM_STC_FROM_STRING = "groovy.transform.stc.FromString";
index b26ce143e48623476cc0eca09b51b2ee359d2750..ab8c68e649f4b8ec32e575ca017a52ee8db9aa13 100644 (file)
@@ -8,13 +8,13 @@ import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.*;
 import com.intellij.psi.codeStyle.CodeStyleManager;
 import com.intellij.psi.impl.light.JavaIdentifier;
-import com.intellij.psi.impl.light.LightElement;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.psi.tree.IElementType;
 import com.intellij.psi.tree.TokenSet;
 import com.intellij.psi.util.*;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.SmartList;
 import com.intellij.util.VisibilityUtil;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.Stack;
@@ -307,10 +307,6 @@ public final class PsiUtil {
   }
 
   public static boolean isAccessible(@NotNull PsiElement place, @NotNull PsiMember member) {
-    if (member instanceof LightElement) {
-      return true;
-    }
-
     return AccessibilityKt.isAccessible(member, place);
   }
 
@@ -1236,6 +1232,18 @@ public final class PsiUtil {
     return false;
   }
 
+  public static @NotNull List<@NotNull PsiAnnotation> getAllAnnotations(@NotNull PsiElement element, @NotNull String annotationNameFq) {
+    List<PsiModifierListOwner> parents = PsiTreeUtil.collectParents(element, PsiModifierListOwner.class, true, __ -> false);
+    SmartList<PsiAnnotation> annotations = new SmartList<>();
+    for (PsiModifierListOwner parent : parents) {
+      PsiAnnotation annotation = parent.getAnnotation(annotationNameFq);
+      if (annotation != null) {
+        annotations.add(annotation);
+      }
+    }
+    return annotations;
+  }
+
   @Nullable
   public static PsiType extractIteratedType(GrForInClause forIn) {
     GrExpression iterated = forIn.getIteratedExpression();
index 95456d429c75f59278151b978e2c752b67f21765..e6421d1625ff7b0cefde576f1e35fafe07494325 100644 (file)
@@ -18,6 +18,7 @@ import org.jetbrains.plugins.groovy.transformations.immutable.GrImmutableUtils;
 import java.util.HashSet;
 import java.util.Set;
 
+import static org.jetbrains.plugins.groovy.lang.resolve.ast.GrVisibilityUtils.getVisibility;
 /**
  * @author peter
  */
@@ -77,6 +78,9 @@ public class ConstructorAnnotationsProcessor implements AstTransformationSupport
     GeneratedConstructorCollector collector = new GeneratedConstructorCollector(tupleConstructor, immutable, fieldsConstructor);
 
     if (tupleConstructor != null) {
+      Visibility visibility = getVisibility(tupleConstructor, fieldsConstructor, Visibility.PUBLIC);
+      fieldsConstructor.addModifier(visibility.toString());
+
       final boolean superFields = PsiUtil.getAnnoAttributeValue(tupleConstructor, "includeSuperFields", false);
       final boolean superProperties = PsiUtil.getAnnoAttributeValue(tupleConstructor, "includeSuperProperties", false);
       if (superFields || superProperties) {
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/GrVisibilityUtils.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/GrVisibilityUtils.kt
new file mode 100644 (file)
index 0000000..444d3be
--- /dev/null
@@ -0,0 +1,66 @@
+// 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.
+@file:JvmName("GrVisibilityUtils")
+
+package org.jetbrains.plugins.groovy.lang.resolve.ast
+
+import com.intellij.psi.*
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier
+import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil.inferStringAttribute
+import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames
+import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil
+import groovy.transform.options.Visibility as GroovyVisibility
+
+enum class Visibility {
+  PRIVATE,
+  PACKAGE_PRIVATE,
+  PROTECTED,
+  PUBLIC;
+
+  override fun toString(): String = when (this) {
+    PRIVATE -> GrModifier.PRIVATE
+    PACKAGE_PRIVATE -> GrModifier.PACKAGE_LOCAL
+    PROTECTED -> GrModifier.PROTECTED
+    PUBLIC -> GrModifier.PUBLIC
+  }
+}
+
+/**
+ * @param annotation: An annotation that has attribute `visibilityId`
+ * @param sourceElement: An element for which visibility will be computed
+ * @param defaultVisibility: [Visibility] that will be returned in case when no suitable visibility annotations found
+ */
+fun getVisibility(annotation: PsiAnnotation, sourceElement: PsiElement, defaultVisibility: Visibility): Visibility {
+  val visAnnotations = PsiUtil.getAllAnnotations(sourceElement.navigationElement, GroovyCommonClassNames.GROOVY_TRANSFORM_VISIBILITY_OPTIONS)
+  val visAnnotationId = inferStringAttribute(annotation, "visibilityId")
+  val targetAnnotation = visAnnotations.firstOrNull { inferStringAttribute(it, "id") == visAnnotationId }
+  if (targetAnnotation == null) return defaultVisibility
+
+  val visibility: GroovyVisibility = when {
+    sourceElement is PsiMethod && sourceElement.isConstructor -> inferGroovyVisibility(targetAnnotation, "constructor")
+    sourceElement is PsiMethod -> inferGroovyVisibility(targetAnnotation, "method")
+    sourceElement is PsiClass -> inferGroovyVisibility(targetAnnotation, "type")
+    else -> null
+  } ?: inferGroovyVisibility(targetAnnotation, "value") ?: return defaultVisibility
+
+  return when (visibility) {
+    GroovyVisibility.UNDEFINED -> defaultVisibility
+    GroovyVisibility.PUBLIC -> Visibility.PUBLIC
+    GroovyVisibility.PROTECTED -> Visibility.PROTECTED
+    GroovyVisibility.PACKAGE_PRIVATE -> Visibility.PACKAGE_PRIVATE
+    GroovyVisibility.PRIVATE -> Visibility.PRIVATE
+  }
+}
+
+private fun inferGroovyVisibility(annotation: PsiAnnotation, attributeName: String) : GroovyVisibility? {
+  val targetValue = annotation.findAttributeValue(attributeName)
+  return if (targetValue is PsiReference) {
+    try {
+      GroovyVisibility.valueOf(targetValue.canonicalText)
+    } catch (e : IllegalArgumentException) {
+      null
+    }
+  } else {
+    null
+  }
+}
+
index 44170f3045a96fdf46d3a083a9ab20197b4d0c1d..8ac1a5fcb10ab8d395bd08d6bbed00a5211d6312 100644 (file)
@@ -154,7 +154,8 @@ static void main(String[] args) {
     highlightingTest """
 @groovy.transform.TupleConstructor(defaults = false)
 class Rr {
-    String actionType = ""
+// todo: defval
+    String actionType
     long referrerCode;
     boolean referrerUrl;
 }
@@ -271,4 +272,26 @@ class Rr {}
 class Rr {}
 """
   }
+
+  @Test
+  void 'test visibility options'() {
+    fixture.addFileToProject 'other.groovy', """
+@groovy.transform.CompileStatic
+@groovy.transform.TupleConstructor(defaults = false)
+@groovy.transform.VisibilityOptions(constructor = Visibility.PRIVATE)
+class Cde {
+    String actionType
+    long referrerCode
+    boolean referrerUrl
+}"""
+    highlightingTest """
+class X {
+
+    @groovy.transform.CompileStatic
+    static void main(String[] args) {
+        def x = new <error>Cde</error>("mem", 1, true)
+    }
+
+}"""
+  }
 }