From e3ecc6282a5fa8fde9fe7657a20280be83307ebe Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Fri, 25 Nov 2016 16:16:09 +0300 Subject: [PATCH] gradle: misc contributor --- plugins/gradle/src/META-INF/plugin.xml | 2 + .../resolve/GradleArtifactsContributor.kt | 4 +- .../resolve/GradleCommonClassNames.java | 3 + .../GradleIdeaPluginScriptContributor.kt | 11 +- .../service/resolve/GradleMiscContributor.kt | 101 ++++++++++++++++++ .../GradleNonCodeMembersContributor.kt | 42 ++++++++ .../resolve/GradleProjectContributor.kt | 7 +- .../service/resolve/GradleResolverUtil.java | 19 ---- .../resolve/GradleScriptContributor.java | 8 +- .../plugins/gradle/service/resolve/util.kt | 48 ++++----- 10 files changed, 181 insertions(+), 64 deletions(-) create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleMiscContributor.kt create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleNonCodeMembersContributor.kt diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index 929aac917e50..2c8ec7438bfb 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -67,6 +67,7 @@ + @@ -170,6 +171,7 @@ + diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleArtifactsContributor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleArtifactsContributor.kt index a2dabe81ec5c..01269ca7ffa8 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleArtifactsContributor.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleArtifactsContributor.kt @@ -31,6 +31,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethod import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightField import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter import org.jetbrains.plugins.groovy.lang.psi.patterns.groovyClosure @@ -57,7 +58,8 @@ class GradleArtifactsContributor : GradleMethodContextContributor { if (methodName != null && place is GrReferenceExpression && psiElement().inside(artifactsClosure).accepts(place)) { val text = place.text if (!methodCallInfo.contains(text) && place is GrReferenceExpression) { - GradleResolverUtil.addImplicitVariable(processor, state, place, JAVA_LANG_OBJECT) + val myPsi = GrLightField(text, JAVA_LANG_OBJECT, place) + processor.execute(myPsi, state) return false } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleCommonClassNames.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleCommonClassNames.java index 732645d7c305..8c809e22b926 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleCommonClassNames.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleCommonClassNames.java @@ -45,6 +45,7 @@ public final class GradleCommonClassNames { @NonNls public static final String GRADLE_API_DISTRIBUTION_CONTAINER = "org.gradle.api.distribution.DistributionContainer"; @NonNls public static final String GRADLE_API_DISTRIBUTION = "org.gradle.api.distribution.Distribution"; @NonNls public static final String GRADLE_API_FILE_COPY_SPEC = "org.gradle.api.file.CopySpec"; + @NonNls public static final String GRADLE_API_FILE_CONFIGURABLE_FILE_TREE = "org.gradle.api.file.ConfigurableFileTree"; @NonNls public static final String GRADLE_API_SCRIPT_HANDLER = "org.gradle.api.initialization.dsl.ScriptHandler"; @NonNls public static final String GRADLE_API_TASK = "org.gradle.api.Task"; @NonNls public static final String GRADLE_API_DEFAULT_TASK = "org.gradle.api.DefaultTask"; @@ -60,6 +61,8 @@ public final class GradleCommonClassNames { @NonNls public static final String GRADLE_API_TASKS_DIAGNOSTICS_PROPERTY_REPORT_TASK = "org.gradle.api.tasks.diagnostics.PropertyReportTask"; @NonNls public static final String GRADLE_API_TASKS_DIAGNOSTICS_TASK_REPORT_TASK = "org.gradle.api.tasks.diagnostics.TaskReportTask"; @NonNls public static final String GRADLE_API_TASKS_TESTING_TEST = "org.gradle.api.tasks.testing.Test"; + @NonNls public static final String GRADLE_API_JUNIT_OPTIONS = "org.gradle.api.tasks.testing.junit.JUnitOptions"; + @NonNls public static final String GRADLE_API_TEST_LOGGING_CONTAINER = "org.gradle.api.tasks.testing.logging.TestLoggingContainer"; @NonNls public static final String GRADLE_API_TASKS_UPLOAD = "org.gradle.api.tasks.Upload"; @NonNls public static final String GRADLE_API_ARTIFACTS_REPOSITORIES_FLAT_DIRECTORY_ARTIFACT_REPOSITORY = "org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository"; @NonNls public static final String GRADLE_LANGUAGE_JVM_TASKS_PROCESS_RESOURCES = "org.gradle.language.jvm.tasks.ProcessResources"; diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleIdeaPluginScriptContributor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleIdeaPluginScriptContributor.kt index 7e9fd14c5490..1d3a8f54360c 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleIdeaPluginScriptContributor.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleIdeaPluginScriptContributor.kt @@ -66,6 +66,12 @@ class GradleIdeaPluginScriptContributor : GradleMethodContextContributor { if (ideaImlClosure.accepts(closure)) { return DelegatesToInfo(TypesUtil.createType(IDEA_MODULE_IML_FQN, closure), Closure.DELEGATE_FIRST) } + if (ideaProjectClosure.accepts(closure)) { + return DelegatesToInfo(TypesUtil.createType(IDEA_PROJECT_FQN, closure), Closure.DELEGATE_FIRST) + } + if (ideaModuleClosure.accepts(closure)) { + return DelegatesToInfo(TypesUtil.createType(IDEA_MODULE_FQN, closure), Closure.DELEGATE_FIRST) + } return null } @@ -86,15 +92,10 @@ class GradleIdeaPluginScriptContributor : GradleMethodContextContributor { } val psiManager = GroovyPsiManager.getInstance(place.project) - if (psiElement().inside(ideaModuleClosure).accepts(place)) { - if (GradleResolverUtil.processDeclarations(psiManager, processor, state, place, IDEA_MODULE_FQN)) return false - } - if (psiElement().inside(ideaProjectClosure).accepts(place)) { if (psiElement().inside(ideaIprClosure).accepts(place)) { if (GradleResolverUtil.processDeclarations(psiManager, processor, state, place, IDE_XML_MERGER_FQN)) return false } - if (GradleResolverUtil.processDeclarations(psiManager, processor, state, place, IDEA_PROJECT_FQN)) return false } return true diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleMiscContributor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleMiscContributor.kt new file mode 100644 index 000000000000..35d8b7a021cb --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleMiscContributor.kt @@ -0,0 +1,101 @@ +/* + * 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.gradle.service.resolve + +import com.intellij.patterns.PsiJavaPatterns.psiElement +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.ResolveState +import com.intellij.psi.scope.PsiScopeProcessor +import groovy.lang.Closure +import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.* +import org.jetbrains.plugins.groovy.lang.psi.GroovyFile +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression +import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder +import org.jetbrains.plugins.groovy.lang.psi.patterns.groovyClosure +import org.jetbrains.plugins.groovy.lang.psi.patterns.psiMethod +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames.GROOVY_LANG_CLOSURE +import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil +import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.DELEGATES_TO_KEY +import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.DelegatesToInfo + +/** + * @author Vladislav.Soroka + * + * @since 11/24/2016 + */ +class GradleMiscContributor : GradleMethodContextContributor { + companion object { + val useJUnitClosure = groovyClosure().inMethod(psiMethod(GRADLE_API_TASKS_TESTING_TEST, "useJUnit")) + val testLoggingClosure = groovyClosure().inMethod(psiMethod(GRADLE_API_TASKS_TESTING_TEST, "testLogging")) + val downloadClosure = groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "download")) + val domainCollectionWithTypeClosure = groovyClosure().inMethod(psiMethod("org.gradle.api.DomainObjectCollection", "withType")) + + val downloadSpecFqn = "de.undercouch.gradle.tasks.download.DownloadSpec" + val pluginDependenciesSpecFqn = "org.gradle.plugin.use.PluginDependenciesSpec" + } + + override fun getDelegatesToInfo(closure: GrClosableBlock): DelegatesToInfo? { + if (useJUnitClosure.accepts(closure)) { + return DelegatesToInfo(TypesUtil.createType(GRADLE_API_JUNIT_OPTIONS, closure), Closure.DELEGATE_FIRST) + } + if (testLoggingClosure.accepts(closure)) { + return DelegatesToInfo(TypesUtil.createType(GRADLE_API_TEST_LOGGING_CONTAINER, closure), Closure.DELEGATE_FIRST) + } + if (downloadClosure.accepts(closure)) { + return DelegatesToInfo(TypesUtil.createType(downloadSpecFqn, closure), Closure.DELEGATE_FIRST) + } + + if (domainCollectionWithTypeClosure.accepts(closure)) { + val parent = closure.parent + if (parent is GrMethodCallExpression) { + val psiElement = parent.argumentList.allArguments.singleOrNull()?.reference?.resolve() + if (psiElement is PsiClass) { + return DelegatesToInfo(TypesUtil.createType(psiElement.qualifiedName, closure), Closure.DELEGATE_FIRST) + } + } + } + return null + } + + override fun process(methodCallInfo: MutableList, processor: PsiScopeProcessor, state: ResolveState, place: PsiElement): Boolean { + + val classHint = processor.getHint(com.intellij.psi.scope.ElementClassHint.KEY) + val shouldProcessMethods = ResolveUtil.shouldProcessMethods(classHint) + val psiManager = GroovyPsiManager.getInstance(place.project) + val resolveScope = place.resolveScope + + if (shouldProcessMethods && place.parent?.parent is GroovyFile && place.text == "plugins") { + val pluginsDependenciesClass = psiManager.findClassWithCache(pluginDependenciesSpecFqn, resolveScope) ?: return true + val returnClass = psiManager.createTypeByFQClassName(pluginDependenciesSpecFqn, resolveScope) ?: return true + val methodBuilder = GrLightMethodBuilder(place.manager, "plugins").apply { + containingClass = pluginsDependenciesClass + returnType = returnClass + } + methodBuilder.addAndGetParameter("configuration", GROOVY_LANG_CLOSURE, false).putUserData(DELEGATES_TO_KEY, pluginDependenciesSpecFqn) + place.putUserData(RESOLVED_CODE, true) + if (!processor.execute(methodBuilder, state)) return false + } + + if (psiElement().inside(domainCollectionWithTypeClosure).accepts(place)) { + + } + return true + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleNonCodeMembersContributor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleNonCodeMembersContributor.kt new file mode 100644 index 000000000000..574284b4f419 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleNonCodeMembersContributor.kt @@ -0,0 +1,42 @@ +/* + * 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.gradle.service.resolve + +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType +import com.intellij.psi.ResolveState +import com.intellij.psi.scope.PsiScopeProcessor +import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor + +/** + * @author Vladislav.Soroka + * + * @since 11/25/2016 + */ +class GradleNonCodeMembersContributor : NonCodeMembersContributor() { + override fun processDynamicElements(qualifierType: PsiType, + aClass: PsiClass?, + processor: PsiScopeProcessor, + place: PsiElement, + state: ResolveState) { + if (aClass == null) return + val containingFile = place.containingFile + if (!containingFile.isGradleScript() || containingFile?.originalFile?.virtualFile == aClass.containingFile?.originalFile?.virtualFile) return + + processDeclarations(aClass, processor, state, place) + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleProjectContributor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleProjectContributor.kt index 12fef0241940..180175ca5944 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleProjectContributor.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleProjectContributor.kt @@ -19,8 +19,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.ResolveState import com.intellij.psi.scope.PsiScopeProcessor import groovy.lang.Closure -import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.GRADLE_API_FILE_COPY_SPEC -import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.GRADLE_API_PROJECT +import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.* import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil @@ -36,6 +35,7 @@ class GradleProjectContributor : GradleMethodContextContributor { companion object { val copySpecClosure = groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "copy", "copySpec")) + val fileTreeClosure = groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "fileTree")) } @@ -43,6 +43,9 @@ class GradleProjectContributor : GradleMethodContextContributor { if (copySpecClosure.accepts(closure)) { return DelegatesToInfo(TypesUtil.createType(GRADLE_API_FILE_COPY_SPEC, closure), Closure.DELEGATE_FIRST) } + if (fileTreeClosure.accepts(closure)) { + return DelegatesToInfo(TypesUtil.createType(GRADLE_API_FILE_CONFIGURABLE_FILE_TREE, closure), Closure.DELEGATE_FIRST) + } return null } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleResolverUtil.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleResolverUtil.java index ff5a5b4bdea5..bf0dc6242340 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleResolverUtil.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleResolverUtil.java @@ -60,25 +60,6 @@ public class GradleResolverUtil { return argsCount; } - public static void addImplicitVariable(@NotNull PsiScopeProcessor processor, - @NotNull ResolveState state, - @NotNull GrReferenceExpressionImpl expression, - @NotNull String type) { - if (expression.getQualifier() == null && expression.getReferenceName() != null) { - PsiVariable myPsi = new GrLightField(expression.getReferenceName(), type, expression); - processor.execute(myPsi, state); - } - } - - public static void addImplicitVariable(@NotNull PsiScopeProcessor processor, - @NotNull ResolveState state, - @NotNull PsiElement element, - @NotNull String type) { - PsiVariable myPsi = new GrLightField(element.getText(), type, element); - processor.execute(myPsi, state); - } - - @Nullable public static GrLightMethodBuilder createMethodWithClosure(@NotNull String name, @Nullable String returnType, diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleScriptContributor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleScriptContributor.java index 185c287d61e1..402211b113ba 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleScriptContributor.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleScriptContributor.java @@ -53,13 +53,7 @@ public class GradleScriptContributor extends NonCodeMembersContributor { @NotNull PsiScopeProcessor processor, @NotNull PsiElement place, @NotNull ResolveState state) { - if (!(aClass instanceof GroovyScriptClass)) { - return; - } - - PsiFile file = aClass.getContainingFile(); - if (file == null || !FileUtilRt.extensionEquals(file.getName(), GradleConstants.EXTENSION) - || GradleConstants.SETTINGS_FILE_NAME.equals(file.getName())) return; + if(!UtilKt.isResolvedInGradleScript(aClass)) return; List methodInfo = ContainerUtilRt.newArrayList(); for (GrMethodCall current = PsiTreeUtil.getParentOfType(place, GrMethodCall.class); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/util.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/util.kt index d7bdcf02e099..d0313f17a367 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/util.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/util.kt @@ -17,10 +17,9 @@ package org.jetbrains.plugins.gradle.service.resolve import com.intellij.openapi.util.Key import com.intellij.psi.* -import com.intellij.psi.scope.BaseScopeProcessor -import com.intellij.psi.scope.ElementClassHint -import com.intellij.psi.scope.NameHint.KEY import com.intellij.psi.scope.PsiScopeProcessor +import org.jetbrains.plugins.gradle.util.GradleConstants.EXTENSION +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass import org.jetbrains.plugins.groovy.lang.psi.patterns.GroovyPatterns import java.util.* @@ -28,51 +27,40 @@ import java.util.* * @author Vladislav.Soroka * @since 11/11/2016 */ +internal fun PsiClass?.isResolvedInGradleScript() = this is GroovyScriptClass && this.containingFile.isGradleScript() + +internal fun PsiFile?.isGradleScript() = this?.originalFile?.virtualFile?.extension == EXTENSION @JvmField val RESOLVED_CODE = Key.create("gradle.resolved") -fun processDeclarations(handlerClass: PsiClass, +fun processDeclarations(aClass: PsiClass, processor: PsiScopeProcessor, state: ResolveState, place: PsiElement): Boolean { - val name = processor.getHint(KEY)?.getName(state) - val componentClass = handlerClass - val componentProcessor = ComponentProcessor(processor, place, name) + val name = processor.getHint(com.intellij.psi.scope.NameHint.KEY)?.getName(state) if (name == null) { - if (!componentClass.processDeclarations(componentProcessor, state, null, place)) return false + aClass.processDeclarations(processor, state, null, place) } else { + val isSetterCandidate = name.startsWith("set") val processedSignatures = HashSet>() - for (method in componentClass.findMethodsByName(name, true)) { - processedSignatures.add(method.getSignature(PsiSubstitutor.EMPTY).parameterTypes.map({ it.canonicalText })) + for (method in aClass.findMethodsByName(name, true)) { + if (!isSetterCandidate) { + processedSignatures.add(method.getSignature(PsiSubstitutor.EMPTY).parameterTypes.map({ it.canonicalText })) + } place.putUserData(RESOLVED_CODE, true) - if (!componentProcessor.execute(method, state)) return false + if (!processor.execute(method, state)) return false } - for (prefix in arrayOf("add", "set")) { - for (method in componentClass.findMethodsByName(prefix + name.capitalize(), true)) { + + if (!isSetterCandidate) { + for (method in aClass.findMethodsByName("set" + name.capitalize(), true)) { if (processedSignatures.contains(method.getSignature(PsiSubstitutor.EMPTY).parameterTypes.map({ it.canonicalText }))) continue place.putUserData(RESOLVED_CODE, true) - if (!componentProcessor.execute(method, state)) return false + if (!processor.execute(method, state)) return false } } } return true } -class ComponentProcessor(val delegate: PsiScopeProcessor, val place: PsiElement, val name: String?) : BaseScopeProcessor() { - - override fun execute(method: PsiElement, state: ResolveState): Boolean { - method as? PsiMethod ?: return true - return delegate.execute(method, state) - } - - override fun getHint(hintKey: Key): T? = if (hintKey == com.intellij.psi.scope.ElementClassHint.KEY) { - @Suppress("UNCHECKED_CAST") - ElementClassHint { it == com.intellij.psi.scope.ElementClassHint.DeclarationKind.METHOD } as T - } - else { - null - } -} - fun psiMethodInClass(containingClass: String) = GroovyPatterns.psiMethod().definedInClass(containingClass) \ No newline at end of file