[groovy] IDEA-207415: Support super constructor call in `pre` attribute
[idea/community.git] / plugins / groovy / groovy-psi / src / org / jetbrains / plugins / groovy / lang / resolve / ast / contributor / SyntheticKeywordConstructorContributor.kt
1 // 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.
2 package org.jetbrains.plugins.groovy.lang.resolve.ast.contributor
3
4 import com.intellij.psi.PsiAnnotation
5 import com.intellij.psi.PsiClass
6 import com.intellij.psi.PsiElement
7 import com.intellij.psi.ResolveState
8 import com.intellij.psi.scope.ElementClassHint
9 import com.intellij.psi.scope.PsiScopeProcessor
10 import com.intellij.psi.util.parentOfType
11 import com.intellij.util.SmartList
12 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock
13 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall
14 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod
15 import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil
16 import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder
17 import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames.GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR
18 import org.jetbrains.plugins.groovy.lang.resolve.ClosureMemberContributor
19 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil
20
21 class SyntheticKeywordConstructorContributor : ClosureMemberContributor() {
22
23   override fun processMembers(closure: GrClosableBlock, processor: PsiScopeProcessor, place: PsiElement, state: ResolveState) {
24     if (!ResolveUtil.shouldProcessMethods(processor.getHint(ElementClassHint.KEY))) return
25     val nameHint = ResolveUtil.getNameHint(processor)
26     if (nameHint != null && nameHint != "super") return
27
28     if (closure != place.parentOfType<GrClosableBlock>()) return
29     val anno = closure.parentOfType<PsiAnnotation>()?.takeIf { it.qualifiedName == GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR } ?: return
30     if (GrAnnotationUtil.inferClosureAttribute(anno, "pre") != closure) return
31
32
33     val syntheticMethods = createSyntheticConstructors(closure)
34
35     for (method in syntheticMethods) {
36       if (!processor.execute(method, state)) {
37         return
38       }
39     }
40   }
41
42   private fun createSyntheticConstructors(closure: GrClosableBlock): List<GrMethod> {
43     val outerClass = closure.parentOfType<PsiClass>() ?: return emptyList()
44     val superClass = outerClass.superClass
45     val methods = SmartList<GrMethod>()
46     if (superClass != null) {
47       val constructors = superClass.constructors
48       if (constructors.isEmpty()) {
49         val method = SyntheticKeywordConstructor(outerClass, superClass, "super")
50         methods.add(method)
51       }
52       else {
53         for (constructor in constructors) {
54           val method = SyntheticKeywordConstructor(outerClass, superClass, "super")
55           for (param in constructor.parameterList.parameters) {
56             method.addParameter(param.name, param.type)
57           }
58           methods.add(method)
59         }
60       }
61     }
62     return methods
63   }
64
65   private class SyntheticKeywordConstructor(containingClass: PsiClass, superClass: PsiClass, name: String) :
66     GrLightMethodBuilder(containingClass.manager, name) {
67     init {
68       assert(name.isReserved())
69       isConstructor = true
70       navigationElement = superClass
71       this.containingClass = containingClass
72     }
73   }
74
75   companion object {
76     private fun String?.isReserved(): Boolean = this == "super"
77
78     @JvmStatic
79     fun isSyntheticConstructorCall(call: GrMethodCall?): Boolean =
80       call?.callReference?.methodName.isReserved() && call?.resolveMethod() is SyntheticKeywordConstructor
81   }
82 }