[groovy] @Singleton support:
authorDaniil Ovchinnikov <daniil.ovchinnikov@jetbrains.com>
Tue, 8 Nov 2016 14:31:18 +0000 (17:31 +0300)
committerDaniil Ovchinnikov <daniil.ovchinnikov@jetbrains.com>
Tue, 8 Nov 2016 15:27:40 +0000 (18:27 +0300)
- implement as AstTransformationSupport instead of gdsl
- add proper members
- support `property` parameter
- support `lazy` parameter

plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/singleton/SingletonTransformationSupport.kt [new file with mode: 0644]
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/singleton/impl.kt [moved from plugins/groovy/groovy-psi/resources/standardDsls/singletonTransform.gdsl with 64% similarity]
plugins/groovy/src/META-INF/plugin.xml
plugins/groovy/test/org/jetbrains/plugins/groovy/dsl/GroovyTransformationsTest.groovy
plugins/groovy/test/org/jetbrains/plugins/groovy/transformations/singleton/SingletonTransformationSupportTest.groovy [new file with mode: 0644]
plugins/groovy/testdata/groovy/dsl/transform/SingletonTransform.groovy [deleted file]
plugins/groovy/testdata/groovy/dsl/transform/SingletonTransform_after.groovy [deleted file]

diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/singleton/SingletonTransformationSupport.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/singleton/SingletonTransformationSupport.kt
new file mode 100644 (file)
index 0000000..6f2dda9
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2000-2016 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.groovy.transformations.singleton
+
+import com.intellij.util.text.nullize
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierFlags.*
+import org.jetbrains.plugins.groovy.lang.psi.impl.booleanValue
+import org.jetbrains.plugins.groovy.lang.psi.impl.findDeclaredDetachedValue
+import org.jetbrains.plugins.groovy.lang.psi.impl.stringValue
+import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightField
+import org.jetbrains.plugins.groovy.transformations.AstTransformationSupport
+import org.jetbrains.plugins.groovy.transformations.TransformationContext
+import org.jetbrains.plugins.groovy.transformations.plusAssign
+
+class SingletonTransformationSupport : AstTransformationSupport {
+
+  override fun applyTransformation(context: TransformationContext) {
+    val annotation = context.getAnnotation(singletonFqn) ?: return
+    val name = annotation.findDeclaredDetachedValue("property").stringValue().nullize(true) ?: "instance"
+    val lazy = annotation.findDeclaredDetachedValue("lazy").booleanValue() ?: false
+
+    context += GrLightField(context.codeClass, name, context.classType, annotation).apply {
+      val modifiers = STATIC_MASK or if (lazy) PRIVATE_MASK else PUBLIC_MASK or FINAL_MASK
+      modifierList.setModifiers(modifiers)
+      navigationElement = annotation
+      originInfo = singletonOriginInfo
+    }
+
+    context += context.memberBuilder.constructor {
+      setModifiers(PRIVATE_MASK)
+      originInfo = singletonOriginInfo
+    }
+
+    context += context.memberBuilder.method("get${name.capitalize()}") {
+      setModifiers(PUBLIC_MASK or STATIC_MASK)
+      returnType = context.classType
+      navigationElement = annotation
+      originInfo = singletonOriginInfo
+    }
+  }
+}
\ No newline at end of file
similarity index 64%
rename from plugins/groovy/groovy-psi/resources/standardDsls/singletonTransform.gdsl
rename to plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/singleton/impl.kt
index d4e2ad6aba45f495b753f862938cd69d09e4eb8c..2d68c7d443e8bcf7a44627e84d926d5da48fab76 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package standardDsls
+package org.jetbrains.plugins.groovy.transformations.singleton
 
-contributor(ctype:hasAnnotation("groovy.lang.Singleton")) {
-  if (psiClass?.hasAnnotation("groovy.lang.Singleton")) {
-    property name: "instance",
-             type: psiClass?.getQualifiedName() ?: "java.lang.Object",
-             isStatic: true
-  }
-}
+import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames
+
+internal val singletonFqn = GroovyCommonClassNames.GROOVY_LANG_SINGLETON
+internal val singletonOriginInfo = "by @Singleton"
\ No newline at end of file
index 46c173d67909c9a13211a2412f14f9288ebee002..1580a5d9dfba4113190d97e20335ac41fb836a2d 100644 (file)
     <astTransformationSupport implementation="org.jetbrains.plugins.groovy.transformations.listenerList.ListenerListTransformationSupport"/>
     <customAnnotationChecker implementation="org.jetbrains.plugins.groovy.transformations.listenerList.ListenerListAnnotationChecker"/>
 
+    <astTransformationSupport implementation="org.jetbrains.plugins.groovy.transformations.singleton.SingletonTransformationSupport"/>
+
     <positionManagerDelegate implementation="org.jetbrains.plugins.groovy.gant.GantPositionManagerHelper"/>
 
     <methodDescriptor lightMethodKey="SwingBuilder_builder_method"
index 286dee97cf69c154bd3f74964846e9e68f0a79ff..1059c81f840d392a71e43ce09f03eb2d796b613b 100644 (file)
@@ -54,10 +54,6 @@ class GroovyTransformationsTest extends LightCodeInsightFixtureTestCase {
 
   void testDelegateAnnotation() throws Throwable { doPlainTest() }
 
-  void testSingletonTransform() throws Throwable {
-    doVariantsTest('instance', 'newInstance', 'newInstance', 'isInstance', 'getInstance', 'setInstance')
-  }
-
   void testCategoryTransform() throws Throwable { doVariantsTest('name', 'getName') }
 
   void testMixinTransform() throws Throwable { doPlainTest() }
diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/transformations/singleton/SingletonTransformationSupportTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/transformations/singleton/SingletonTransformationSupportTest.groovy
new file mode 100644 (file)
index 0000000..3da2960
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2000-2016 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.groovy.transformations.singleton
+
+import com.intellij.testFramework.LightProjectDescriptor
+import groovy.transform.CompileStatic
+import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor
+import org.jetbrains.plugins.groovy.LightGroovyTestCase
+import org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess.GrUnresolvedAccessInspection
+import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyFileImpl
+
+@CompileStatic
+class SingletonTransformationSupportTest extends LightGroovyTestCase {
+
+  final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST
+
+  void 'test highlighting'() {
+    fixture.with {
+      def file = addFileToProject('singletons.groovy', '''\
+@Singleton
+class Simple {}
+
+@Singleton(property = "coolInstance")
+class CustomName {}
+
+@Singleton(strict = false)
+class DefaultConstructor {
+    DefaultConstructor() {}
+}
+
+@Singleton(strict = false)
+class CustomConstructor {
+    CustomConstructor(int a, String b) {}
+}
+
+@Singleton(lazy = true)
+class Lazy {}
+
+@Singleton(strict = false, lazy = true)
+class LazyDefaultConstructor {
+    LazyDefaultConstructor() {}
+}
+''') as GroovyFileImpl
+
+      configureByText '_.groovy', '''\
+Simple.<warning descr="Cannot resolve symbol 'setInstance'">setInstance</warning>(null)
+Simple simple
+simple = Simple.getInstance()
+simple = Simple.instance
+simple = new Simple()
+
+CustomName.<warning descr="Cannot resolve symbol 'setCoolInstance'">setCoolInstance</warning>(null)
+CustomName customName
+customName = CustomName.getCoolInstance()
+customName = CustomName.coolInstance
+customName = new CustomName()
+
+DefaultConstructor.<warning descr="Cannot resolve symbol 'setInstance'">setInstance</warning>(null)
+DefaultConstructor defaultConstructor
+defaultConstructor = DefaultConstructor.getInstance()
+defaultConstructor = DefaultConstructor.instance
+defaultConstructor = new DefaultConstructor()
+
+CustomConstructor customConstructor
+customConstructor = CustomConstructor.getInstance()
+customConstructor = CustomConstructor.instance
+customConstructor = new CustomConstructor()
+customConstructor = new CustomConstructor(1, "")
+
+Lazy lazy
+lazy = Lazy.getInstance()
+lazy = Lazy.instance
+lazy = new Lazy()
+
+LazyDefaultConstructor lazyDefaultConstructor
+lazyDefaultConstructor = LazyDefaultConstructor.getInstance()
+lazyDefaultConstructor = LazyDefaultConstructor.instance
+lazyDefaultConstructor = new LazyDefaultConstructor()
+'''
+      enableInspections(GrUnresolvedAccessInspection)
+      checkHighlighting()
+
+      configureByText 'Main.java', '''\
+public class Main {
+  public static void main(String[] args) {
+    Simple simple;
+    simple = Simple.getInstance();
+    simple = Simple.instance;
+    simple = new <error descr="'Simple()' has private access in 'Simple'">Simple</error>();
+
+    CustomName customName;
+    customName = CustomName.getCoolInstance();
+    customName = CustomName.coolInstance;
+    customName = new <error descr="'CustomName()' has private access in 'CustomName'">CustomName</error>();
+
+    DefaultConstructor defaultConstructor;
+    defaultConstructor = DefaultConstructor.getInstance();
+    defaultConstructor = DefaultConstructor.instance;
+    defaultConstructor = new DefaultConstructor();
+
+    CustomConstructor customConstructor;
+    customConstructor = CustomConstructor.getInstance();
+    customConstructor = CustomConstructor.instance;
+    customConstructor = new <error descr="'CustomConstructor()' has private access in 'CustomConstructor'">CustomConstructor</error>();
+    customConstructor = new CustomConstructor(1, "");
+
+    Lazy lazy;
+    lazy = Lazy.getInstance();
+    lazy = Lazy.<error descr="'instance' has private access in 'Lazy'">instance</error>;
+    lazy = new <error descr="'Lazy()' has private access in 'Lazy'">Lazy</error>();
+
+    LazyDefaultConstructor lazyDefaultConstructor;
+    lazyDefaultConstructor = LazyDefaultConstructor.getInstance();
+    lazyDefaultConstructor = LazyDefaultConstructor.<error descr="'instance' has private access in 'LazyDefaultConstructor'">instance</error>;
+    lazyDefaultConstructor = new LazyDefaultConstructor();
+  }
+}
+'''
+      checkHighlighting()
+      assert !file.contentsLoaded
+    }
+  }
+}
diff --git a/plugins/groovy/testdata/groovy/dsl/transform/SingletonTransform.groovy b/plugins/groovy/testdata/groovy/dsl/transform/SingletonTransform.groovy
deleted file mode 100644 (file)
index ba22e17..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-@Singleton class T {}
-
-T.inst<caret>
\ No newline at end of file
diff --git a/plugins/groovy/testdata/groovy/dsl/transform/SingletonTransform_after.groovy b/plugins/groovy/testdata/groovy/dsl/transform/SingletonTransform_after.groovy
deleted file mode 100644 (file)
index 97ee38e..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-@Singleton class T {}
-
-T.instance
\ No newline at end of file