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;
|
assert functionDescriptor.getReturnType() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resolveConstructorParameterDefaultValuesAndAnnotations(
|
public void resolveConstructorParameterDefaultValues(
|
||||||
@NotNull DataFlowInfo outerDataFlowInfo,
|
@NotNull DataFlowInfo outerDataFlowInfo,
|
||||||
@NotNull BindingTrace trace,
|
@NotNull BindingTrace trace,
|
||||||
@NotNull KtClass klass,
|
@NotNull KtPrimaryConstructor constructor,
|
||||||
@NotNull ConstructorDescriptor constructorDescriptor,
|
@NotNull ConstructorDescriptor constructorDescriptor,
|
||||||
@NotNull LexicalScope declaringScope
|
@NotNull LexicalScope declaringScope
|
||||||
) {
|
) {
|
||||||
List<KtParameter> valueParameters = klass.getPrimaryConstructorParameters();
|
List<KtParameter> valueParameters = constructor.getValueParameters();
|
||||||
List<ValueParameterDescriptor> valueParameterDescriptors = constructorDescriptor.getValueParameters();
|
List<ValueParameterDescriptor> valueParameterDescriptors = constructorDescriptor.getValueParameters();
|
||||||
|
|
||||||
LexicalScope scope = getPrimaryConstructorParametersScope(declaringScope, constructorDescriptor);
|
LexicalScope scope = getPrimaryConstructorParametersScope(declaringScope, constructorDescriptor);
|
||||||
|
|||||||
@@ -236,9 +236,9 @@ class ResolveElementCache(
|
|||||||
element,
|
element,
|
||||||
KtNamedFunction::class.java,
|
KtNamedFunction::class.java,
|
||||||
KtAnonymousInitializer::class.java,
|
KtAnonymousInitializer::class.java,
|
||||||
|
KtPrimaryConstructor::class.java,
|
||||||
KtSecondaryConstructor::class.java,
|
KtSecondaryConstructor::class.java,
|
||||||
KtProperty::class.java,
|
KtProperty::class.java,
|
||||||
KtParameter::class.java,
|
|
||||||
KtSuperTypeList::class.java,
|
KtSuperTypeList::class.java,
|
||||||
KtInitializerList::class.java,
|
KtInitializerList::class.java,
|
||||||
KtImportList::class.java,
|
KtImportList::class.java,
|
||||||
@@ -265,15 +265,6 @@ class ResolveElementCache(
|
|||||||
|
|
||||||
is KtPackageDirective -> return element
|
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 -> {
|
is KtDeclaration -> {
|
||||||
if (element is KtParameter && !KtPsiUtil.isLocal(element)) {
|
if (element is KtParameter && !KtPsiUtil.isLocal(element)) {
|
||||||
return null
|
return null
|
||||||
@@ -316,6 +307,8 @@ class ResolveElementCache(
|
|||||||
|
|
||||||
is KtAnonymousInitializer -> initializerAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter(), bodyResolveMode.bindingTraceFilter)
|
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 KtSecondaryConstructor -> secondaryConstructorAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter(), bodyResolveMode.bindingTraceFilter)
|
||||||
|
|
||||||
is KtProperty -> propertyAdditionalResolve(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 KtAnnotationEntry -> annotationAdditionalResolve(resolveSession, resolveElement)
|
||||||
|
|
||||||
is KtClass -> constructorAdditionalResolve(resolveSession, resolveElement, file, bodyResolveMode.bindingTraceFilter)
|
|
||||||
|
|
||||||
is KtTypeAlias -> typealiasAdditionalResolve(resolveSession, resolveElement, bodyResolveMode.bindingTraceFilter)
|
is KtTypeAlias -> typealiasAdditionalResolve(resolveSession, resolveElement, bodyResolveMode.bindingTraceFilter)
|
||||||
|
|
||||||
is KtTypeParameter -> typeParameterAdditionalResolve(resolveSession, resolveElement)
|
is KtTypeParameter -> typeParameterAdditionalResolve(resolveSession, resolveElement)
|
||||||
@@ -548,8 +539,13 @@ class ResolveElementCache(
|
|||||||
?: error("Can't get primary constructor for descriptor '$classDescriptor' " +
|
?: error("Can't get primary constructor for descriptor '$classDescriptor' " +
|
||||||
"in from class '${klass.getElementTextWithContext()}'")
|
"in from class '${klass.getElementTextWithContext()}'")
|
||||||
|
|
||||||
val bodyResolver = createBodyResolver(resolveSession, trace, file, StatementFilter.NONE)
|
val primaryConstructor = klass.getPrimaryConstructor()
|
||||||
bodyResolver.resolveConstructorParameterDefaultValuesAndAnnotations(DataFlowInfo.EMPTY, trace, klass, constructorDescriptor, scope)
|
if (primaryConstructor != null) {
|
||||||
|
val bodyResolver = createBodyResolver(resolveSession, trace, file, StatementFilter.NONE)
|
||||||
|
bodyResolver.resolveConstructorParameterDefaultValues(DataFlowInfo.EMPTY, trace, primaryConstructor, constructorDescriptor, scope)
|
||||||
|
|
||||||
|
forceResolveAnnotationsInside(primaryConstructor)
|
||||||
|
}
|
||||||
|
|
||||||
return trace
|
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
|
settingManager.temporarySettings = tempSettings
|
||||||
try {
|
try {
|
||||||
val fileText = FileUtil.loadFile(File(testPath))
|
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 invocationCount = InTextDirectivesUtils.getPrefixedInt(fileText, INVOCATION_COUNT_PREFIX) ?: 1
|
||||||
val lookupString = InTextDirectivesUtils.findStringWithPrefixes(fileText, LOOKUP_STRING_PREFIX)
|
val lookupString = InTextDirectivesUtils.findStringWithPrefixes(fileText, LOOKUP_STRING_PREFIX)
|
||||||
val itemText = InTextDirectivesUtils.findStringWithPrefixes(fileText, ELEMENT_TEXT_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");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/annotation/AnnotationInCompanionObjectAddImport.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("idea/idea-completion/testData/handlers/basic/callableReference")
|
||||||
|
|||||||
+4
@@ -105,6 +105,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() {
|
|||||||
doTest()
|
doTest()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testKT12077() {
|
||||||
|
doTest()
|
||||||
|
}
|
||||||
|
|
||||||
fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) {
|
fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) {
|
||||||
val fileName = getTestName(false)
|
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.analyzeFullyAndGetResult
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
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.KotlinLightCodeInsightFixtureTestCase
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
@@ -365,6 +366,69 @@ class C(param1: String = "", param2: Int = 0) {
|
|||||||
annotationArguments.analyzeFullyAndGetResult()
|
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() {
|
fun testFullResolveMultiple() {
|
||||||
doTest {
|
doTest {
|
||||||
val aBody = (members[0] as KtFunction).bodyExpression as KtBlockExpression
|
val aBody = (members[0] as KtFunction).bodyExpression as KtBlockExpression
|
||||||
|
|||||||
@@ -89,6 +89,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
|||||||
doTestWithExtraFile(fileName);
|
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")
|
@TestMetadata("delegateExtensionBoth.test")
|
||||||
public void testDelegateExtensionBoth() throws Exception {
|
public void testDelegateExtensionBoth() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/delegateExtensionBoth.test");
|
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);
|
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")
|
@TestMetadata("ClassNameConflict.kt")
|
||||||
public void testClassNameConflict() throws Exception {
|
public void testClassNameConflict() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/shortenRefs/ClassNameConflict.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/shortenRefs/ClassNameConflict.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user