KT-12077 Code completion inserts FQN for annotations with use-site target inside primary constructor
KT-13009 Kotlin: annotation auto-import is broken in multiple ways #KT-12077 Fixed #KT-13009 Fixed
This commit is contained in:
@@ -832,14 +832,14 @@ public class BodyResolver {
|
||||
assert functionDescriptor.getReturnType() != null;
|
||||
}
|
||||
|
||||
public void resolveConstructorParameterDefaultValuesAndAnnotations(
|
||||
public void resolveConstructorParameterDefaultValues(
|
||||
@NotNull DataFlowInfo outerDataFlowInfo,
|
||||
@NotNull BindingTrace trace,
|
||||
@NotNull KtClass klass,
|
||||
@NotNull KtPrimaryConstructor constructor,
|
||||
@NotNull ConstructorDescriptor constructorDescriptor,
|
||||
@NotNull LexicalScope declaringScope
|
||||
) {
|
||||
List<KtParameter> valueParameters = klass.getPrimaryConstructorParameters();
|
||||
List<KtParameter> valueParameters = constructor.getValueParameters();
|
||||
List<ValueParameterDescriptor> valueParameterDescriptors = constructorDescriptor.getValueParameters();
|
||||
|
||||
LexicalScope scope = getPrimaryConstructorParametersScope(declaringScope, constructorDescriptor);
|
||||
|
||||
@@ -236,9 +236,9 @@ class ResolveElementCache(
|
||||
element,
|
||||
KtNamedFunction::class.java,
|
||||
KtAnonymousInitializer::class.java,
|
||||
KtPrimaryConstructor::class.java,
|
||||
KtSecondaryConstructor::class.java,
|
||||
KtProperty::class.java,
|
||||
KtParameter::class.java,
|
||||
KtSuperTypeList::class.java,
|
||||
KtInitializerList::class.java,
|
||||
KtImportList::class.java,
|
||||
@@ -265,15 +265,6 @@ class ResolveElementCache(
|
||||
|
||||
is KtPackageDirective -> return element
|
||||
|
||||
is KtParameter -> {
|
||||
val klass = elementOfAdditionalResolve.getParentOfType<KtClass>(strict = true)
|
||||
if (klass != null && elementOfAdditionalResolve.getParent() == klass.getPrimaryConstructorParameterList()) {
|
||||
return klass
|
||||
}
|
||||
|
||||
return elementOfAdditionalResolve
|
||||
}
|
||||
|
||||
is KtDeclaration -> {
|
||||
if (element is KtParameter && !KtPsiUtil.isLocal(element)) {
|
||||
return null
|
||||
@@ -316,6 +307,8 @@ class ResolveElementCache(
|
||||
|
||||
is KtAnonymousInitializer -> initializerAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter(), bodyResolveMode.bindingTraceFilter)
|
||||
|
||||
is KtPrimaryConstructor -> constructorAdditionalResolve(resolveSession, resolveElement.parent as KtClass, file, bodyResolveMode.bindingTraceFilter)
|
||||
|
||||
is KtSecondaryConstructor -> secondaryConstructorAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter(), bodyResolveMode.bindingTraceFilter)
|
||||
|
||||
is KtProperty -> propertyAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter(), bodyResolveMode.bindingTraceFilter)
|
||||
@@ -342,8 +335,6 @@ class ResolveElementCache(
|
||||
|
||||
is KtAnnotationEntry -> annotationAdditionalResolve(resolveSession, resolveElement)
|
||||
|
||||
is KtClass -> constructorAdditionalResolve(resolveSession, resolveElement, file, bodyResolveMode.bindingTraceFilter)
|
||||
|
||||
is KtTypeAlias -> typealiasAdditionalResolve(resolveSession, resolveElement, bodyResolveMode.bindingTraceFilter)
|
||||
|
||||
is KtTypeParameter -> typeParameterAdditionalResolve(resolveSession, resolveElement)
|
||||
@@ -548,8 +539,13 @@ class ResolveElementCache(
|
||||
?: error("Can't get primary constructor for descriptor '$classDescriptor' " +
|
||||
"in from class '${klass.getElementTextWithContext()}'")
|
||||
|
||||
val bodyResolver = createBodyResolver(resolveSession, trace, file, StatementFilter.NONE)
|
||||
bodyResolver.resolveConstructorParameterDefaultValuesAndAnnotations(DataFlowInfo.EMPTY, trace, klass, constructorDescriptor, scope)
|
||||
val primaryConstructor = klass.getPrimaryConstructor()
|
||||
if (primaryConstructor != null) {
|
||||
val bodyResolver = createBodyResolver(resolveSession, trace, file, StatementFilter.NONE)
|
||||
bodyResolver.resolveConstructorParameterDefaultValues(DataFlowInfo.EMPTY, trace, primaryConstructor, constructorDescriptor, scope)
|
||||
|
||||
forceResolveAnnotationsInside(primaryConstructor)
|
||||
}
|
||||
|
||||
return trace
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package some
|
||||
|
||||
annotation class SomeAnnotation
|
||||
|
||||
class Complete(@set:Some<caret> var field: Int)
|
||||
|
||||
// ELEMENT: SomeAnnotation
|
||||
@@ -0,0 +1,7 @@
|
||||
package some
|
||||
|
||||
annotation class SomeAnnotation
|
||||
|
||||
class Complete(@set:SomeAnnotation<caret> var field: Int)
|
||||
|
||||
// ELEMENT: SomeAnnotation
|
||||
@@ -0,0 +1,3 @@
|
||||
package some
|
||||
|
||||
class Complete(@set:SomeAnn<caret> var field: Int)
|
||||
@@ -0,0 +1,3 @@
|
||||
package other
|
||||
|
||||
annotation class SomeAnnotation
|
||||
@@ -0,0 +1,5 @@
|
||||
package some
|
||||
|
||||
import other.SomeAnnotation
|
||||
|
||||
class Complete(@set:SomeAnnotation<caret> var field: Int)
|
||||
+1
@@ -42,6 +42,7 @@ abstract class AbstractCompletionHandlerTest(private val defaultCompletionType:
|
||||
settingManager.temporarySettings = tempSettings
|
||||
try {
|
||||
val fileText = FileUtil.loadFile(File(testPath))
|
||||
assertTrue("\"<caret>\" is missing in file \"$testPath\"", fileText.contains("<caret>"));
|
||||
val invocationCount = InTextDirectivesUtils.getPrefixedInt(fileText, INVOCATION_COUNT_PREFIX) ?: 1
|
||||
val lookupString = InTextDirectivesUtils.findStringWithPrefixes(fileText, LOOKUP_STRING_PREFIX)
|
||||
val itemText = InTextDirectivesUtils.findStringWithPrefixes(fileText, ELEMENT_TEXT_PREFIX)
|
||||
|
||||
+6
@@ -228,6 +228,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/annotation/AnnotationInCompanionObjectAddImport.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("KT12077.kt")
|
||||
public void testKT12077() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/annotation/KT12077.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/handlers/basic/callableReference")
|
||||
|
||||
+4
@@ -105,6 +105,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testKT12077() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) {
|
||||
val fileName = getTestName(false)
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// FILE: first.before.kt
|
||||
// "Import" "true"
|
||||
// ERROR: Unresolved reference: SomeAnnotation
|
||||
|
||||
package some
|
||||
|
||||
class X(@set:SomeAnnotation<caret> var field: Int)
|
||||
//-----------------------
|
||||
// FILE: second.kt
|
||||
|
||||
package other
|
||||
|
||||
annotation class SomeAnnotation
|
||||
//-----------------------
|
||||
// FILE: first.after.kt
|
||||
// "Import" "true"
|
||||
// ERROR: Unresolved reference: SomeAnnotation
|
||||
|
||||
package some
|
||||
|
||||
import other.SomeAnnotation
|
||||
|
||||
class X(@set:SomeAnnotation<caret> var field: Int)
|
||||
//-----------------------
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package some
|
||||
|
||||
annotation class SomeAnnotation
|
||||
|
||||
class Complete(@set:<selection>some.SomeAnnotation</selection> var field: Int)
|
||||
@@ -0,0 +1,5 @@
|
||||
package some
|
||||
|
||||
annotation class SomeAnnotation
|
||||
|
||||
class Complete(@set:SomeAnnotation var field: Int)
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
@@ -365,6 +366,69 @@ class C(param1: String = "", param2: Int = 0) {
|
||||
annotationArguments.analyzeFullyAndGetResult()
|
||||
}
|
||||
|
||||
fun testFunctionParameterAnnotation() {
|
||||
val file = myFixture.configureByText("Test.kt", """
|
||||
annotation class Ann
|
||||
fun foo(@Ann p: Int) {
|
||||
bar()
|
||||
}
|
||||
""") as KtFile
|
||||
|
||||
val function = (file.declarations[1]) as KtFunction
|
||||
val annotationEntry = function.valueParameters[0].annotationEntries[0]
|
||||
val typeRef = annotationEntry.typeReference!!
|
||||
|
||||
val bindingContext = typeRef.analyze(BodyResolveMode.PARTIAL)
|
||||
|
||||
val referenceExpr = (typeRef.typeElement as KtUserType).referenceExpression
|
||||
val target = bindingContext[BindingContext.REFERENCE_TARGET, referenceExpr]
|
||||
TestCase.assertEquals("Ann", target?.importableFqName?.asString())
|
||||
|
||||
val statement = (function.bodyExpression as KtBlockExpression).statements[0]
|
||||
TestCase.assertEquals(null, bindingContext[BindingContext.PROCESSED, statement])
|
||||
}
|
||||
|
||||
fun testPrimaryConstructorParameterAnnotation() {
|
||||
val file = myFixture.configureByText("Test.kt", """
|
||||
annotation class Ann
|
||||
class X(@set:Ann var p: Int)
|
||||
""") as KtFile
|
||||
|
||||
val constructor = ((file.declarations[1]) as KtClass).getPrimaryConstructor()!!
|
||||
val annotationEntry = constructor.valueParameters[0].annotationEntries[0]
|
||||
val typeRef = annotationEntry.typeReference!!
|
||||
|
||||
val bindingContext = typeRef.analyze(BodyResolveMode.PARTIAL)
|
||||
|
||||
val referenceExpr = (typeRef.typeElement as KtUserType).referenceExpression
|
||||
val target = bindingContext[BindingContext.REFERENCE_TARGET, referenceExpr]
|
||||
TestCase.assertEquals("Ann", target?.importableFqName?.asString())
|
||||
}
|
||||
|
||||
fun testSecondaryConstructorParameterAnnotation() {
|
||||
val file = myFixture.configureByText("Test.kt", """
|
||||
annotation class Ann
|
||||
class X {
|
||||
constructor(@Ann p: Int) {
|
||||
foo()
|
||||
}
|
||||
}
|
||||
""") as KtFile
|
||||
|
||||
val constructor = ((file.declarations[1]) as KtClass).getSecondaryConstructors()[0]
|
||||
val annotationEntry = constructor.valueParameters[0].annotationEntries[0]
|
||||
val typeRef = annotationEntry.typeReference!!
|
||||
|
||||
val bindingContext = typeRef.analyze(BodyResolveMode.PARTIAL)
|
||||
|
||||
val referenceExpr = (typeRef.typeElement as KtUserType).referenceExpression
|
||||
val target = bindingContext[BindingContext.REFERENCE_TARGET, referenceExpr]
|
||||
TestCase.assertEquals("Ann", target?.importableFqName?.asString())
|
||||
|
||||
val statement = (constructor.bodyExpression as KtBlockExpression).statements[0]
|
||||
TestCase.assertEquals(null, bindingContext[BindingContext.PROCESSED, statement])
|
||||
}
|
||||
|
||||
fun testFullResolveMultiple() {
|
||||
doTest {
|
||||
val aBody = (members[0] as KtFunction).bodyExpression as KtBlockExpression
|
||||
|
||||
@@ -89,6 +89,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
||||
doTestWithExtraFile(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("constructorParameterAnnotation.test")
|
||||
public void testConstructorParameterAnnotation() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/constructorParameterAnnotation.test");
|
||||
doTestWithExtraFile(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("delegateExtensionBoth.test")
|
||||
public void testDelegateExtensionBoth() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/delegateExtensionBoth.test");
|
||||
|
||||
@@ -35,6 +35,12 @@ public class ShortenRefsTestGenerated extends AbstractShortenRefsTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/shortenRefs"), Pattern.compile("^([^.]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("annotation.kt")
|
||||
public void testAnnotation() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/shortenRefs/annotation.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ClassNameConflict.kt")
|
||||
public void testClassNameConflict() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/shortenRefs/ClassNameConflict.kt");
|
||||
|
||||
Reference in New Issue
Block a user