junit 5: support references between kotlin and java (IDEA-248355)

GitOrigin-RevId: 7db4cb465500beed1e79f0262a785c0653424d12
This commit is contained in:
Olga.Klisho
2020-10-27 13:12:36 +01:00
committed by intellij-monorepo-bot
parent a4022dc65d
commit 339e65fcd2
5 changed files with 113 additions and 37 deletions

View File

@@ -11,5 +11,6 @@
<orderEntry type="library" scope="RUNTIME" name="KotlinPlugin" level="project" />
<orderEntry type="library" scope="TEST" name="kotlin-stdlib-jdk8" level="project" />
<orderEntry type="module" module-name="intellij.java.tests" scope="TEST" />
<orderEntry type="module" module-name="intellij.junit" scope="TEST" />
</component>
</module>

View File

@@ -0,0 +1,8 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
class KtMethodSourceUsage {
@ParameterizedTest
@MethodSource("SampleTest#squares")
void testSquares(int input, int expected) {}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2000-2017 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 com.intellij.codeInspection
import com.intellij.execution.junit.codeInsight.JUnit5MalformedParameterizedInspection
import com.intellij.execution.junit.codeInsight.JUnit5TestFrameworkSetupUtil
import com.intellij.jvm.analysis.JvmAnalysisKtTestsUtil
import com.intellij.testFramework.LightProjectDescriptor
import com.siyeh.ig.LightJavaInspectionTestCase
class KotlinJUnit5MalformedParameterizedTest : LightJavaInspectionTestCase() {
override fun getInspection(): InspectionProfileEntry? {
return JUnit5MalformedParameterizedInspection()
}
@Throws(Exception::class)
override fun setUp() {
super.setUp()
myFixture.addFileToProject("kotlin/jvm/JvmStatic.kt",
"package kotlin.jvm public annotation class JvmStatic")
myFixture.addFileToProject("SampleTest.kt", """open class SampleTest {
companion object {
@kotlin.jvm.JvmStatic
fun squares() : List<org.junit.jupiter.params.provider.Arguments> {
return listOf(
org.junit.jupiter.params.provider.Arguments.of(1, 1)
)
}
}
}""")
JUnit5TestFrameworkSetupUtil.setupJUnit5Library(myFixture)
}
fun testKtMethodSourceUsage() {
doTest()
}
override fun getBasePath() =
"${JvmAnalysisKtTestsUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH}/codeInspection/junit5malformed"
override fun getProjectDescriptor(): LightProjectDescriptor {
return JAVA_8
}
}

View File

@@ -10,20 +10,20 @@ import com.intellij.psi.*;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.position.FilterPattern;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ProcessingContext;
import com.siyeh.ig.junit.JUnitCommonClassNames;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.uast.*;
public class JUnitReferenceContributor extends PsiReferenceContributor {
private static PsiElementPattern.Capture<PsiLiteral> getElementPattern(String annotation, String paramName) {
return PlatformPatterns.psiElement(PsiLiteral.class).and(new FilterPattern(new TestAnnotationFilter(annotation, paramName)));
private static PsiElementPattern.Capture<PsiElement> getElementPattern(String annotation, String paramName) {
return PlatformPatterns.psiElement(PsiElement.class).and(new FilterPattern(new TestAnnotationFilter(annotation, paramName)));
}
private static PsiElementPattern.Capture<PsiLiteral> getEnumSourceNamesPattern() {
private static PsiElementPattern.Capture<PsiElement> getEnumSourceNamesPattern() {
return getElementPattern(JUnitCommonClassNames.ORG_JUNIT_JUPITER_PARAMS_ENUM_SOURCE, "names")
.withAncestor(4, PlatformPatterns.psiElement(PsiAnnotation.class).and(new PsiJavaElementPattern<>(new InitialPatternCondition<PsiAnnotation>(PsiAnnotation.class) {
@Override
@@ -45,7 +45,7 @@ public class JUnitReferenceContributor extends PsiReferenceContributor {
registrar.registerReferenceProvider(getElementPattern(JUnitCommonClassNames.ORG_JUNIT_JUPITER_PARAMS_PROVIDER_METHOD_SOURCE, "value"), new PsiReferenceProvider() {
@Override
public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) {
return new MethodSourceReference[]{new MethodSourceReference((PsiLiteral)element)};
return new MethodSourceReference[]{new MethodSourceReference(UastContextKt.toUElement(element, UExpression.class), (PsiLanguageInjectionHost)element)};
}
});
registrar.registerReferenceProvider(getEnumSourceNamesPattern(), new PsiReferenceProvider() {
@@ -74,18 +74,20 @@ public class JUnitReferenceContributor extends PsiReferenceContributor {
@Override
public boolean isAcceptable(Object element, PsiElement context) {
PsiNameValuePair pair = PsiTreeUtil.getParentOfType(context, PsiNameValuePair.class, false, PsiMember.class, PsiStatement.class, PsiCall.class);
if (pair == null) return false;
String name = ObjectUtils.notNull(pair.getName(), PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME);
UElement type = UastContextKt.toUElement(context, UElement.class);
if (type == null) return false;
UNamedExpression uPair = UastUtils.getParentOfType(type, UNamedExpression.class);
if (uPair == null) return false;
String name = ObjectUtils.notNull(uPair.getName(), PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME);
if (!myParameterName.equals(name)) return false;
PsiAnnotation annotation = PsiTreeUtil.getParentOfType(pair, PsiAnnotation.class);
if (annotation == null) return false;
return myAnnotation.equals(annotation.getQualifiedName());
UAnnotation uAnnotation = UastUtils.getParentOfType(type, UAnnotation.class);
if (uAnnotation == null) return false;
return myAnnotation.equals(uAnnotation.getQualifiedName());
}
@Override
public boolean isClassAcceptable(Class hintClass) {
return PsiLiteral.class.isAssignableFrom(hintClass);
return PsiLanguageInjectionHost.class.isAssignableFrom(hintClass);
}
}
}

View File

@@ -20,11 +20,14 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.ClassUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ig.psiutils.TestUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.uast.UClass;
import org.jetbrains.uast.UExpression;
import org.jetbrains.uast.UMethod;
import org.jetbrains.uast.UastUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -32,10 +35,12 @@ import java.util.List;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_OBJECT;
public class MethodSourceReference extends PsiReferenceBase<PsiLiteral> {
public class MethodSourceReference extends PsiReferenceBase<PsiLanguageInjectionHost> {
private final UExpression myLiteral;
public MethodSourceReference(PsiLiteral element) {
public MethodSourceReference(UExpression uLiteral, PsiLanguageInjectionHost element) {
super(element, false);
myLiteral = uLiteral;
}
@Override
@@ -51,47 +56,49 @@ public class MethodSourceReference extends PsiReferenceBase<PsiLiteral> {
String methodName = getValue();
String className = StringUtil.getPackageName(methodName, '#');
boolean selfClassReference = className.isEmpty() ||
ClassUtil.findPsiClass(getElement().getManager(), className, null, false, getElement().getResolveScope()) == null;
ClassUtil
.findPsiClass(getElement().getManager(), className, null, false, getElement().getResolveScope()) == null;
return super.handleElementRename(selfClassReference ? newElementName : className + '#' + newElementName);
}
@Override
@Nullable
public PsiElement resolve() {
PsiClass cls = PsiTreeUtil.getParentOfType(getElement(), PsiClass.class);
if (cls != null) {
String methodName = getValue();
String className = StringUtil.getPackageName(methodName, '#');
if (!className.isEmpty()) {
PsiClass aClass = ClassUtil.findPsiClass(cls.getManager(), className, null, false, cls.getResolveScope());
if (aClass != null) {
cls = aClass;
methodName = StringUtil.getShortName(methodName, '#');
}
UClass clazz = UastUtils.getParentOfType(myLiteral, UClass.class);
if (clazz == null) return null;
PsiClass psiClazz = clazz.getPsi();
String methodName = (String)myLiteral.evaluate();
if (methodName == null) return null;
String className = StringUtil.getPackageName(methodName, '#');
if (!className.isEmpty()) {
PsiClass aClass = ClassUtil.findPsiClass(psiClazz.getManager(), className, null, false, psiClazz.getResolveScope());
if (aClass != null) {
psiClazz = aClass;
methodName = StringUtil.getShortName(methodName, '#');
}
PsiMethod[] methods = cls.findMethodsByName(methodName, true);
final PsiClass finalCls = cls;
return Arrays.stream(methods)
.filter(method -> staticOrOneInstancePerClassNoParams(method, finalCls))
.findFirst()
.orElse(methods.length == 0 ? null : methods[0]);
}
return null;
PsiMethod[] clazzMethods = psiClazz.findMethodsByName(methodName, true);
final PsiClass finalCls = psiClazz;
return Arrays.stream(clazzMethods)
.filter(method -> staticOrOneInstancePerClassNoParams(method, finalCls))
.findFirst()
.orElse(clazzMethods.length == 0 ? null : clazzMethods[0]);
}
@Override
public Object @NotNull [] getVariants() {
final List<Object> list = new ArrayList<>();
final PsiClass topLevelClass = PsiTreeUtil.getParentOfType(getElement(), PsiClass.class);
final UClass topLevelClass = UastUtils.getParentOfType(myLiteral, UClass.class);
if (topLevelClass != null) {
final PsiMethod current = PsiTreeUtil.getParentOfType(getElement(), PsiMethod.class);
final PsiMethod[] methods = topLevelClass.getAllMethods();
final UMethod current = UastUtils.getParentOfType(myLiteral, UMethod.class);
PsiClass psiTopLevelClass = topLevelClass.getJavaPsi();
final PsiMethod[] methods = psiTopLevelClass.getAllMethods();
for (PsiMethod method : methods) {
PsiClass aClass = method.getContainingClass();
if (aClass == null) continue;
if (JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) continue;
if (current != null && method.getName().equals(current.getName())) continue;
if (!staticOrOneInstancePerClassNoParams(method, topLevelClass)) continue;
if (!staticOrOneInstancePerClassNoParams(method, psiTopLevelClass)) continue;
final LookupElementBuilder builder = LookupElementBuilder.create(method);
list.add(builder.withAutoCompletionPolicy(AutoCompletionPolicy.SETTINGS_DEPENDENT));
}