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:
Valentin Kipyatkov
2016-10-10 19:32:33 +03:00
parent 0c14058d13
commit 73dc5fefcb
16 changed files with 159 additions and 17 deletions
@@ -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)
@@ -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)
@@ -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")
@@ -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
View File
@@ -0,0 +1,5 @@
package some
annotation class SomeAnnotation
class Complete(@set:<selection>some.SomeAnnotation</selection> var field: Int)
+5
View File
@@ -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");