diff --git a/jvm/jvm-analysis-kotlin-tests/intellij.jvm.analysis.kotlin.tests.iml b/jvm/jvm-analysis-kotlin-tests/intellij.jvm.analysis.kotlin.tests.iml index 19fadb196974..4f8fc8a391ce 100644 --- a/jvm/jvm-analysis-kotlin-tests/intellij.jvm.analysis.kotlin.tests.iml +++ b/jvm/jvm-analysis-kotlin-tests/intellij.jvm.analysis.kotlin.tests.iml @@ -11,5 +11,6 @@ + \ No newline at end of file diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/junit5malformed/KtMethodSourceUsage.java b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/junit5malformed/KtMethodSourceUsage.java new file mode 100644 index 000000000000..800c274bc165 --- /dev/null +++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/junit5malformed/KtMethodSourceUsage.java @@ -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) {} +} diff --git a/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/KotlinJUnit5MalformedParameterizedTest.kt b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/KotlinJUnit5MalformedParameterizedTest.kt new file mode 100644 index 000000000000..de0617bc8a86 --- /dev/null +++ b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/KotlinJUnit5MalformedParameterizedTest.kt @@ -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 { + 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 + } +} \ No newline at end of file diff --git a/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java b/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java index 4a675c3958d9..e9e443c77219 100644 --- a/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java +++ b/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java @@ -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 getElementPattern(String annotation, String paramName) { - return PlatformPatterns.psiElement(PsiLiteral.class).and(new FilterPattern(new TestAnnotationFilter(annotation, paramName))); + private static PsiElementPattern.Capture getElementPattern(String annotation, String paramName) { + return PlatformPatterns.psiElement(PsiElement.class).and(new FilterPattern(new TestAnnotationFilter(annotation, paramName))); } - private static PsiElementPattern.Capture getEnumSourceNamesPattern() { + private static PsiElementPattern.Capture getEnumSourceNamesPattern() { return getElementPattern(JUnitCommonClassNames.ORG_JUNIT_JUPITER_PARAMS_ENUM_SOURCE, "names") .withAncestor(4, PlatformPatterns.psiElement(PsiAnnotation.class).and(new PsiJavaElementPattern<>(new InitialPatternCondition(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); } } } diff --git a/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/MethodSourceReference.java b/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/MethodSourceReference.java index bbd1b7fe477b..596ffe238d2a 100644 --- a/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/MethodSourceReference.java +++ b/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/MethodSourceReference.java @@ -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 { +public class MethodSourceReference extends PsiReferenceBase { + 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 { 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 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)); }