mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[gradle] process conventions' members as additional members of Project receiver
This fixes `project.someProperty` cases where `someProperty` is a property of some convention. Also conventions doesn't support setter invocation via method call, so they are ignored.
This commit is contained in:
@@ -62,6 +62,7 @@
|
||||
<membersContributor implementation="org.jetbrains.plugins.gradle.service.resolve.GradleNonCodeMembersContributor"/>
|
||||
<membersContributor implementation="org.jetbrains.plugins.gradle.service.resolve.GradleScriptContributor"/>
|
||||
<membersContributor implementation="org.jetbrains.plugins.gradle.service.resolve.GradleSetterAsMethodContributor"/>
|
||||
<membersContributor implementation="org.jetbrains.plugins.gradle.service.resolve.GradleConventionsContributor"/>
|
||||
<delegatesToProvider implementation="org.jetbrains.plugins.gradle.service.resolve.GradleDelegatesToProvider"/>
|
||||
<referenceTypeEnhancer implementation="org.jetbrains.plugins.gradle.service.resolve.NamedDomainObjectCollectionTypeEnhancer"/>
|
||||
<unresolvedHighlightFilter implementation="org.jetbrains.plugins.gradle.service.resolve.GradleUnresolvedReferenceFilter"/>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2000-2019 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.gradle.service.resolve
|
||||
|
||||
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.gradle.service.resolve.GradleCommonClassNames.*
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor
|
||||
|
||||
class GradleConventionsContributor : NonCodeMembersContributor() {
|
||||
|
||||
companion object {
|
||||
val conventions = arrayOf(
|
||||
GRADLE_API_BASE_PLUGIN_CONVENTION,
|
||||
GRADLE_API_JAVA_PLUGIN_CONVENTION,
|
||||
GRADLE_API_APPLICATION_PLUGIN_CONVENTION,
|
||||
GRADLE_API_WAR_CONVENTION
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin conventions are available on the project instance, be it top level reference or `project.something`.
|
||||
*/
|
||||
override fun getParentClassName(): String = GRADLE_API_PROJECT
|
||||
|
||||
override fun processDynamicElements(qualifierType: PsiType, processor: PsiScopeProcessor, place: PsiElement, state: ResolveState) {
|
||||
// TODO process conventions from Gradle import instead of hardcoded ones
|
||||
processDelegatedDeclarations(processor, state, place, *conventions)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
// 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-2019 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.gradle.service.resolve
|
||||
|
||||
import com.intellij.patterns.PsiJavaElementPattern
|
||||
import com.intellij.patterns.PsiJavaPatterns.psiElement
|
||||
import com.intellij.patterns.StandardPatterns.or
|
||||
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.api.statements.blocks.GrClosableBlock
|
||||
@@ -20,6 +18,7 @@ import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.DelegatesToInfo
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
class GradleJavaContributor : GradleMethodContextContributor {
|
||||
|
||||
companion object {
|
||||
val sourceSetsClosure: GroovyClosurePattern = groovyClosure().inMethod(psiMethod(GRADLE_API_JAVA_PLUGIN_CONVENTION, "sourceSets"))
|
||||
val sourceDirectorySetClosure: PsiJavaElementPattern.Capture<PsiElement> = psiElement().andOr(
|
||||
@@ -36,16 +35,4 @@ class GradleJavaContributor : GradleMethodContextContributor {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun process(methodCallInfo: List<String>, processor: PsiScopeProcessor, state: ResolveState, place: PsiElement): Boolean {
|
||||
if (!GradleResolverUtil.processDeclarations(processor, state, place,
|
||||
GRADLE_API_BASE_PLUGIN_CONVENTION,
|
||||
GRADLE_API_JAVA_PLUGIN_CONVENTION,
|
||||
GRADLE_API_APPLICATION_PLUGIN_CONVENTION,
|
||||
GRADLE_API_WAR_CONVENTION)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,9 @@ class GradleNonCodeMembersContributor : NonCodeMembersContributor() {
|
||||
extensionsData.findProperty(propCandidate)?.let(processVariable)
|
||||
}
|
||||
else {
|
||||
processDeclarations(aClass, processor, state, place)
|
||||
if (aClass.qualifiedName !in GradleConventionsContributor.conventions) {
|
||||
processDeclarations(aClass, processor, state, place)
|
||||
}
|
||||
val propCandidate = place.references.singleOrNull()?.canonicalText ?: return
|
||||
val domainObjectType = (qualifierType.superTypes.firstOrNull { it is PsiClassType } as? PsiClassType)?.parameters?.singleOrNull()
|
||||
?: return
|
||||
|
||||
@@ -37,11 +37,16 @@ internal fun PsiFile?.isGradleScript() = this?.originalFile?.virtualFile?.extens
|
||||
val RESOLVED_CODE: Key<Boolean?> = Key.create("gradle.resolved")
|
||||
|
||||
// TODO extract API for delegation
|
||||
fun processDelegatedDeclarations(processor: PsiScopeProcessor, state: ResolveState, place: PsiElement, fqn: String): Boolean {
|
||||
fun processDelegatedDeclarations(processor: PsiScopeProcessor, state: ResolveState, place: PsiElement, vararg fqns: String): Boolean {
|
||||
val javaPsiFacade = JavaPsiFacade.getInstance(place.project)
|
||||
val clazz = javaPsiFacade.findClass(fqn, place.resolveScope) ?: return true
|
||||
val type = javaPsiFacade.elementFactory.createType(clazz)
|
||||
return type.processReceiverType(processor, state, place)
|
||||
for (fqn in fqns) {
|
||||
val clazz = javaPsiFacade.findClass(fqn, place.resolveScope) ?: continue
|
||||
val type = javaPsiFacade.elementFactory.createType(clazz)
|
||||
if (!type.processReceiverType(processor, state, place)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun processDeclarations(aClass: PsiClass,
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2000-2019 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.gradle.dsl
|
||||
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.testFramework.RunAll
|
||||
import groovy.transform.CompileStatic
|
||||
import org.jetbrains.plugins.gradle.highlighting.GradleHighlightingBaseTest
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall
|
||||
import org.jetbrains.plugins.groovy.util.ResolveTest
|
||||
import org.junit.Test
|
||||
|
||||
import static org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.GRADLE_API_JAVA_PLUGIN_CONVENTION
|
||||
|
||||
@CompileStatic
|
||||
class GradleConventionsTest extends GradleHighlightingBaseTest implements ResolveTest {
|
||||
|
||||
// The test should fail when conventions would be taken from Gradle import
|
||||
// instead of being processed in org.jetbrains.plugins.gradle.service.resolve.GradleConventionsContributor unconditionally.
|
||||
// In such case `apply plugin: 'java'` is required to fix the test.
|
||||
@Test
|
||||
void test() {
|
||||
importProject("")
|
||||
new RunAll().append {
|
||||
'property read'()
|
||||
} append {
|
||||
'property read via project'()
|
||||
} append {
|
||||
'property write'()
|
||||
} append {
|
||||
'setter method'()
|
||||
} run()
|
||||
}
|
||||
|
||||
void 'property read'() {
|
||||
doTest('<caret>docsDir') {
|
||||
methodTest(resolveTest(PsiMethod), 'getDocsDir', GRADLE_API_JAVA_PLUGIN_CONVENTION)
|
||||
}
|
||||
}
|
||||
|
||||
void 'property read via project'() {
|
||||
doTest('project.<caret>docsDir') {
|
||||
methodTest(resolveTest(PsiMethod), 'getDocsDir', GRADLE_API_JAVA_PLUGIN_CONVENTION)
|
||||
}
|
||||
}
|
||||
|
||||
void 'property write'() {
|
||||
doTest('<caret>sourceCompatibility = 42') {
|
||||
methodTest(resolveTest(PsiMethod), 'setSourceCompatibility', GRADLE_API_JAVA_PLUGIN_CONVENTION)
|
||||
}
|
||||
}
|
||||
|
||||
void 'setter method'() {
|
||||
doTest('<caret>targetCompatibility("1.8")') {
|
||||
def call = elementUnderCaret(GrMethodCall)
|
||||
def result = call.advancedResolve()
|
||||
assert result.invokedOnProperty
|
||||
// getTargetCompatibility() is resolved, just because it exists, but later it's highlighted with warning
|
||||
methodTest(assertInstanceOf(result.element, PsiMethod), 'getTargetCompatibility', GRADLE_API_JAVA_PLUGIN_CONVENTION)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2000-2019 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.util;
|
||||
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -20,7 +22,13 @@ public interface ResolveTest extends BaseTest {
|
||||
|
||||
@NotNull
|
||||
default <T extends PsiReference> T referenceByText(@NotNull String text, @NotNull Class<T> refType) {
|
||||
PsiReference ref = configureByText(text).findReferenceAt(getFixture().getCaretOffset());
|
||||
configureByText(text);
|
||||
return referenceUnderCaret(refType);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
default <T extends PsiReference> T referenceUnderCaret(@NotNull Class<T> refType) {
|
||||
PsiReference ref = getGroovyFile().findReferenceAt(getFixture().getCaretOffset());
|
||||
return assertInstanceOf(ref, refType);
|
||||
}
|
||||
|
||||
@@ -43,6 +51,10 @@ public interface ResolveTest extends BaseTest {
|
||||
return resolveTest(referenceByText(text), clazz);
|
||||
}
|
||||
|
||||
default <T extends PsiElement> T resolveTest(@Nullable Class<T> clazz) {
|
||||
return resolveTest(referenceUnderCaret(GroovyReference.class), clazz);
|
||||
}
|
||||
|
||||
default <T extends PsiElement> T resolveTest(@NotNull GroovyReference reference, @Nullable Class<T> clazz) {
|
||||
Collection<? extends GroovyResolveResult> results = reference.resolve(false);
|
||||
if (clazz == null) {
|
||||
@@ -62,4 +74,16 @@ public interface ResolveTest extends BaseTest {
|
||||
assertType(fqn, delegatesToInfo.getTypeToDelegate());
|
||||
assertEquals(strategy, delegatesToInfo.getStrategy());
|
||||
}
|
||||
|
||||
default void methodTest(@NotNull PsiMethod method, String name, String fqn) {
|
||||
assertEquals(name, method.getName());
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
if (fqn == null) {
|
||||
assertNull(containingClass);
|
||||
}
|
||||
else {
|
||||
assertNotNull(containingClass);
|
||||
assertEquals(fqn, containingClass.getQualifiedName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user