FIR checker: report unresolved reference in imports
Compared to FE1.0, instead of using UNRESOLVED_REFERENCE, a new UNRESOLVED_IMPORT diagnostic is introduced. This is so that we can use a different positioning strategy to highlight the last part of the import if the entire import is passed. Also, this change fixed some incorrectly rejected imports in FIR. Such cases are covered the newly added test file staticFunAndPropertyImport.kt
This commit is contained in:
committed by
teamcityserver
parent
f51b108821
commit
18f7a760bb
+6
@@ -26011,6 +26011,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/scopes/inheritance/statics/overloadStatic.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("staticFunAndPropertyImport.kt")
|
||||
public void testStaticFunAndPropertyImport() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/scopes/inheritance/statics/staticFunAndPropertyImport.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("staticFunVsImport.kt")
|
||||
public void testStaticFunVsImport() throws Exception {
|
||||
|
||||
+6
@@ -26011,6 +26011,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/scopes/inheritance/statics/overloadStatic.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("staticFunAndPropertyImport.kt")
|
||||
public void testStaticFunAndPropertyImport() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/scopes/inheritance/statics/staticFunAndPropertyImport.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("staticFunVsImport.kt")
|
||||
public void testStaticFunVsImport() throws Exception {
|
||||
|
||||
+3
@@ -123,6 +123,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val UNRESOLVED_REFERENCE_WRONG_RECEIVER by error<PsiElement> {
|
||||
parameter<Collection<Symbol>>("candidates")
|
||||
}
|
||||
val UNRESOLVED_IMPORT by error<PsiElement>(PositioningStrategy.IMPORT_LAST_NAME) {
|
||||
parameter<String>("reference")
|
||||
}
|
||||
}
|
||||
|
||||
val CALL_RESOLUTION by object : DiagnosticGroup("Call resolution") {
|
||||
|
||||
@@ -149,6 +149,7 @@ object FirErrors {
|
||||
val DEPRECATION_ERROR by error2<PsiElement, FirBasedSymbol<*>, String>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val DEPRECATION by warning2<PsiElement, FirBasedSymbol<*>, String>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val UNRESOLVED_REFERENCE_WRONG_RECEIVER by error1<PsiElement, Collection<FirBasedSymbol<*>>>()
|
||||
val UNRESOLVED_IMPORT by error1<PsiElement, String>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
|
||||
|
||||
// Call resolution
|
||||
val CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS by error0<KtExpression>()
|
||||
|
||||
@@ -10,33 +10,33 @@ import com.intellij.psi.tree.IElementType
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import org.jetbrains.kotlin.*
|
||||
|
||||
fun KtSourceElement.getChild(type: IElementType, index: Int = 0, depth: Int = -1): KtSourceElement? {
|
||||
return getChild(setOf(type), index, depth)
|
||||
fun KtSourceElement.getChild(type: IElementType, index: Int = 0, depth: Int = -1, reverse: Boolean = false): KtSourceElement? {
|
||||
return getChild(setOf(type), index, depth, reverse)
|
||||
}
|
||||
|
||||
fun KtSourceElement.getChild(types: TokenSet, index: Int = 0, depth: Int = -1): KtSourceElement? {
|
||||
return getChild(types.types.toSet(), index, depth)
|
||||
fun KtSourceElement.getChild(types: TokenSet, index: Int = 0, depth: Int = -1, reverse: Boolean = false): KtSourceElement? {
|
||||
return getChild(types.types.toSet(), index, depth, reverse)
|
||||
}
|
||||
|
||||
fun KtSourceElement.getChild(types: Set<IElementType>, index: Int = 0, depth: Int = -1): KtSourceElement? {
|
||||
fun KtSourceElement.getChild(types: Set<IElementType>, index: Int = 0, depth: Int = -1, reverse: Boolean = false): KtSourceElement? {
|
||||
return when (this) {
|
||||
is KtPsiSourceElement -> {
|
||||
getChild(types, index, depth)
|
||||
getChild(types, index, depth, reverse)
|
||||
}
|
||||
is KtLightSourceElement -> {
|
||||
getChild(types, index, depth)
|
||||
getChild(types, index, depth, reverse)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtPsiSourceElement.getChild(types: Set<IElementType>, index: Int, depth: Int): KtSourceElement? {
|
||||
val visitor = PsiElementFinderByType(types, index, depth)
|
||||
private fun KtPsiSourceElement.getChild(types: Set<IElementType>, index: Int, depth: Int, reverse: Boolean): KtSourceElement? {
|
||||
val visitor = PsiElementFinderByType(types, index, depth, reverse)
|
||||
return visitor.find(psi)?.toKtPsiSourceElement()
|
||||
}
|
||||
|
||||
private fun KtLightSourceElement.getChild(types: Set<IElementType>, index: Int, depth: Int): KtSourceElement? {
|
||||
val visitor = LighterTreeElementFinderByType(treeStructure, types, index, depth)
|
||||
private fun KtLightSourceElement.getChild(types: Set<IElementType>, index: Int, depth: Int, reverse: Boolean): KtSourceElement? {
|
||||
val visitor = LighterTreeElementFinderByType(treeStructure, types, index, depth, reverse)
|
||||
val childNode = visitor.find(lighterASTNode) ?: return null
|
||||
return buildChildSourceElement(childNode)
|
||||
}
|
||||
|
||||
+4
-2
@@ -14,7 +14,8 @@ class LighterTreeElementFinderByType(
|
||||
private val tree: FlyweightCapableTreeStructure<LighterASTNode>,
|
||||
private var types: Collection<IElementType>,
|
||||
private var index: Int,
|
||||
private val depth: Int
|
||||
private val depth: Int,
|
||||
private val reverse: Boolean,
|
||||
) {
|
||||
fun find(node: LighterASTNode?): LighterASTNode? {
|
||||
if (node == null) return null
|
||||
@@ -33,7 +34,8 @@ class LighterTreeElementFinderByType(
|
||||
|
||||
if (currentDepth == depth) return null
|
||||
|
||||
for (child in node.getChildren()) {
|
||||
val children = if (reverse) node.getChildren().asReversed() else node.getChildren()
|
||||
for (child in children) {
|
||||
val result = visitNode(child, currentDepth + 1)
|
||||
if (result != null) return result
|
||||
}
|
||||
|
||||
+4
-2
@@ -12,7 +12,8 @@ import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||
class PsiElementFinderByType(
|
||||
private val types: Collection<IElementType>,
|
||||
private var index: Int,
|
||||
private val depth: Int
|
||||
private val depth: Int,
|
||||
private val reverse: Boolean,
|
||||
) {
|
||||
fun find(root: PsiElement): PsiElement? {
|
||||
return visitElement(root, 0)
|
||||
@@ -30,7 +31,8 @@ class PsiElementFinderByType(
|
||||
|
||||
if (currentDepth == depth) return null
|
||||
|
||||
for (child in element.allChildren) {
|
||||
val children = if (reverse) element.allChildren.toList().asReversed().iterator() else element.allChildren.iterator()
|
||||
for (child in children) {
|
||||
val result = visitElement(child, currentDepth + 1)
|
||||
if (result != null) return result
|
||||
}
|
||||
|
||||
+1
@@ -120,6 +120,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirTopLevelFunctionsChecker,
|
||||
FirTopLevelPropertiesChecker,
|
||||
FirImportsChecker,
|
||||
FirUnresolvedInMiddleOfImportChecker,
|
||||
)
|
||||
|
||||
override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker>
|
||||
|
||||
+97
-36
@@ -11,15 +11,14 @@ import org.jetbrains.kotlin.fir.analysis.checkers.collectEnumEntries
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirDeprecationChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.fullyExpandedClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.FirErrorImport
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.FirImport
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isOperator
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||
@@ -63,23 +62,37 @@ object FirImportsChecker : FirFileChecker() {
|
||||
//empty name come from LT in some erroneous cases
|
||||
if (importedName.isSpecial || importedName.identifier.isEmpty()) return
|
||||
|
||||
val classId = (import as? FirResolvedImport)?.resolvedParentClassId
|
||||
if (classId != null) {
|
||||
val classSymbol = classId.resolveToClass(context) ?: return
|
||||
if (classSymbol.classKind.isSingleton) return
|
||||
val symbolProvider = context.session.symbolProvider
|
||||
val parentClassId = (import as? FirResolvedImport)?.resolvedParentClassId
|
||||
if (parentClassId != null) {
|
||||
val parentClassSymbol = parentClassId.resolveToClass(context) ?: return
|
||||
|
||||
if (!classSymbol.canBeImported(context, importedName)) {
|
||||
reporter.reportOn(import.source, FirErrors.CANNOT_BE_IMPORTED, importedName, context)
|
||||
when (val status = parentClassSymbol.getImportStatusOfCallableMembers(context, importedName)) {
|
||||
ImportStatus.OK -> return
|
||||
else -> {
|
||||
val classId = parentClassSymbol.classId.createNestedClassId(importedName)
|
||||
if (symbolProvider.getClassLikeSymbolByClassId(classId) != null) return
|
||||
if (status == ImportStatus.UNRESOLVED) {
|
||||
reporter.reportOn(import.source, FirErrors.UNRESOLVED_IMPORT, importedName.asString(), context)
|
||||
} else {
|
||||
reporter.reportOn(import.source, FirErrors.CANNOT_BE_IMPORTED, importedName, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val importedClassId = ClassId.topLevel(importedFqName)
|
||||
if (importedClassId.resolveToClass(context) != null
|
||||
|| context.session.symbolProvider.getTopLevelCallableSymbols(importedFqName.parent(), importedName).isNotEmpty()
|
||||
) {
|
||||
return
|
||||
}
|
||||
context.session.symbolProvider.getPackage(importedFqName)?.let {
|
||||
reporter.reportOn(import.source, FirErrors.PACKAGE_CANNOT_BE_IMPORTED, context)
|
||||
val resolvedToClass = importedClassId.resolveToClass(context) != null
|
||||
val resolvedToSymbols = symbolProvider.getTopLevelCallableSymbols(importedFqName.parent(), importedName).isNotEmpty()
|
||||
val resolvedToPackages = symbolProvider.getPackage(importedFqName) != null
|
||||
when {
|
||||
resolvedToClass || resolvedToSymbols -> return
|
||||
resolvedToPackages -> reporter.reportOn(import.source, FirErrors.PACKAGE_CANNOT_BE_IMPORTED, context)
|
||||
else -> reporter.reportOn(
|
||||
import.source,
|
||||
FirErrors.UNRESOLVED_IMPORT,
|
||||
importedName.asString(),
|
||||
context,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,9 +101,9 @@ object FirImportsChecker : FirFileChecker() {
|
||||
val interestingImports = imports
|
||||
.filterIsInstance<FirResolvedImport>()
|
||||
.filter { import ->
|
||||
!import.isAllUnder
|
||||
&& import.importedName?.identifierOrNullIfSpecial?.isNotEmpty() == true
|
||||
&& import.resolvesToClass(context)
|
||||
!import.isAllUnder &&
|
||||
import.importedName?.identifierOrNullIfSpecial?.isNotEmpty() == true &&
|
||||
import.resolvesToClass(context)
|
||||
}
|
||||
interestingImports
|
||||
.groupBy { it.aliasName ?: it.importedName!! }
|
||||
@@ -160,26 +173,74 @@ object FirImportsChecker : FirFileChecker() {
|
||||
return result
|
||||
}
|
||||
|
||||
private fun FirRegularClassSymbol.canBeImported(
|
||||
private enum class ImportStatus {
|
||||
OK,
|
||||
CANNOT_BE_IMPORTED,
|
||||
UNRESOLVED
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun FirRegularClassSymbol.getImportStatusOfCallableMembers(context: CheckerContext, name: Name): ImportStatus {
|
||||
return if (classKind.isSingleton) {
|
||||
getImportStatusOfCallableMembersFromSingleton(context, name)
|
||||
} else {
|
||||
getImportStatusOfCallableMembersFromNonSingleton(context, name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirRegularClassSymbol.getImportStatusOfCallableMembersFromSingleton(
|
||||
context: CheckerContext,
|
||||
name: Name
|
||||
): Boolean {
|
||||
name: Name,
|
||||
): ImportStatus {
|
||||
// Use declaredMemberScope first because it's faster and it's relatively rare to import members declared from super types.
|
||||
for (scope in listOf(context.session.declaredMemberScope(this), unsubstitutedScope(context))) {
|
||||
var found = false
|
||||
scope.processFunctionsByName(name) {
|
||||
found = true
|
||||
}
|
||||
if (found) return ImportStatus.OK
|
||||
|
||||
scope.processPropertiesByName(name) {
|
||||
found = true
|
||||
}
|
||||
if (found) return ImportStatus.OK
|
||||
}
|
||||
return ImportStatus.UNRESOLVED
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun FirRegularClassSymbol.getImportStatusOfCallableMembersFromNonSingleton(
|
||||
context: CheckerContext,
|
||||
name: Name,
|
||||
): ImportStatus {
|
||||
var hasStatic = false
|
||||
var hasIllegal = false
|
||||
val scope = context.session.declaredMemberScope(this)
|
||||
scope.processFunctionsByName(name) { sym ->
|
||||
if (sym.isStatic) hasStatic = true
|
||||
else hasIllegal = true
|
||||
}
|
||||
if (hasStatic) return true
|
||||
if (hasIllegal) return false
|
||||
var found = false
|
||||
for (scope in listOfNotNull(
|
||||
// We first try resolution with declaredMemberScope because it's faster and typically imported members are not from
|
||||
// super types.
|
||||
context.session.declaredMemberScope(this),
|
||||
|
||||
scope.processPropertiesByName(name) { sym ->
|
||||
if (sym.isStatic) hasStatic = true
|
||||
else hasIllegal = true
|
||||
}
|
||||
// Next, we try static scope, which can provide static (Java) members from super classes. Note that it's not available
|
||||
// for pure Kotlin classes.
|
||||
fir.staticScope(context.sessionHolder),
|
||||
|
||||
return hasStatic || !hasIllegal
|
||||
// Finally, we fallback to unsubstitutedScope to catch all
|
||||
unsubstitutedScope(context)
|
||||
)) {
|
||||
scope.processFunctionsByName(name) { sym ->
|
||||
if (sym.isStatic) hasStatic = true
|
||||
found = true
|
||||
}
|
||||
if (hasStatic) return ImportStatus.OK
|
||||
|
||||
scope.processPropertiesByName(name) { sym ->
|
||||
if (sym.isStatic) hasStatic = true
|
||||
found = true
|
||||
}
|
||||
if (hasStatic) return ImportStatus.OK
|
||||
}
|
||||
return if (found) ImportStatus.CANNOT_BE_IMPORTED
|
||||
else ImportStatus.UNRESOLVED
|
||||
}
|
||||
|
||||
private fun checkDeprecatedImport(import: FirImport, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.syntax
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.collectEnumEntries
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.getChild
|
||||
import org.jetbrains.kotlin.fir.declarations.FirErrorImport
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedParentInImport
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
object FirUnresolvedInMiddleOfImportChecker : FirDeclarationSyntaxChecker<FirFile, KtFile>() {
|
||||
override fun checkPsiOrLightTree(element: FirFile, source: KtSourceElement, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
for (import in element.imports) {
|
||||
if (import is FirErrorImport) processErrorImport(import, context, reporter)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processErrorImport(import: FirErrorImport, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
when (val diagnostic = import.diagnostic) {
|
||||
is ConeUnresolvedParentInImport -> {
|
||||
val source = import.source ?: return
|
||||
var segmentSource: KtSourceElement? = source.dotQualifiedExpression() ?: return
|
||||
|
||||
val symbolProvider = context.session.symbolProvider
|
||||
val parentClassId = diagnostic.parentClassId
|
||||
|
||||
if (import.isAllUnder && isClassIdPointingToEnumEntry(parentClassId, symbolProvider)) {
|
||||
// Enum entries cannot be resolved as class so star import of enum falls in here and we treat it as
|
||||
// CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON,
|
||||
parentClassId.shortClassName,
|
||||
context,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// For star import, the PSI structure has one less level of qualified references. Hence we start from 0. Otherwise, we start
|
||||
// from 1 to skip the last imported name.
|
||||
var errorSegmentIndexFromLast = if (import.isAllUnder) 0 else 1
|
||||
var currentClassId = parentClassId.parentClassId
|
||||
while (currentClassId != null && symbolProvider.getClassLikeSymbolByClassId(currentClassId) == null) {
|
||||
currentClassId = currentClassId.parentClassId
|
||||
errorSegmentIndexFromLast++
|
||||
}
|
||||
// Finds the right segment from the last that causes resolution to fail. Note that FirImportResolveTransformer always create
|
||||
// an error import with resolvable package segments. That is, the segment corresponding to the outermost class name is where
|
||||
// resolution failed.
|
||||
for (i in 1..errorSegmentIndexFromLast) {
|
||||
segmentSource = segmentSource?.dotQualifiedExpression()
|
||||
}
|
||||
val unresolvedSource = segmentSource?.selectorExpression() ?: return
|
||||
reporter.reportOn(
|
||||
unresolvedSource,
|
||||
FirErrors.UNRESOLVED_IMPORT,
|
||||
parentClassId.getOutermostClassName(),
|
||||
context,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the short outermost class name. For example, `foo.bar.Foo` -> `Foo`. `foo.bar.Outer.Inner.InnerAgain` -> `Outer`.
|
||||
*/
|
||||
private fun ClassId.getOutermostClassName() = relativeClassName.pathSegments().first().asString()
|
||||
|
||||
private fun isClassIdPointingToEnumEntry(classId: ClassId, symbolProvider: FirSymbolProvider): Boolean {
|
||||
val enumClassId = classId.parentClassId ?: return false
|
||||
val enumClass =
|
||||
(symbolProvider.getClassLikeSymbolByClassId(enumClassId) as? FirRegularClassSymbol)?.takeIf { it.isEnumClass } ?: return false
|
||||
return enumClass.collectEnumEntries().any { it.callableId.callableName == classId.shortClassName }
|
||||
}
|
||||
|
||||
private fun KtSourceElement.dotQualifiedExpression() = getChild(KtNodeTypes.DOT_QUALIFIED_EXPRESSION, depth = 1)
|
||||
private fun KtSourceElement.selectorExpression() = getChild(KtNodeTypes.REFERENCE_EXPRESSION, depth = 1, reverse = true)
|
||||
}
|
||||
+2
@@ -476,6 +476,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_LATEI
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_NOT_NULL_ASSERTION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_SAFE_CALL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNREACHABLE_CODE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_IMPORT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_LABEL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_REFERENCE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_REFERENCE_WRONG_RECEIVER
|
||||
@@ -578,6 +579,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
NAME_OF_CONTAINING_DECLARATION_OR_FILE
|
||||
)
|
||||
map.put(UNRESOLVED_REFERENCE, "Unresolved reference: {0}", NULLABLE_STRING)
|
||||
map.put(UNRESOLVED_IMPORT, "Unresolved reference: {0}", NULLABLE_STRING) // &
|
||||
map.put(UNRESOLVED_LABEL, "Unresolved label")
|
||||
map.put(DESERIALIZATION_ERROR, "Deserialization error")
|
||||
map.put(ERROR_FROM_JAVA_RESOLUTION, "Java resolution error")
|
||||
|
||||
+1
@@ -113,6 +113,7 @@ private fun ConeDiagnostic.toKtDiagnostic(
|
||||
is ConeUnderscoreIsReserved -> FirErrors.UNDERSCORE_IS_RESERVED.createOn(this.source)
|
||||
is ConeUnderscoreUsageWithoutBackticks -> FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.createOn(this.source)
|
||||
is ConeAmbiguousSuper -> FirErrors.AMBIGUOUS_SUPER.createOn(source, this.candidateTypes)
|
||||
is ConeUnresolvedParentInImport -> null // reported in FirUnresolvedImportChecker
|
||||
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
|
||||
}
|
||||
|
||||
|
||||
+18
-1
@@ -194,7 +194,12 @@ class DeclarationsConverter(
|
||||
var aliasSource: KtSourceElement? = null
|
||||
importDirective.forEachChildren {
|
||||
when (it.tokenType) {
|
||||
DOT_QUALIFIED_EXPRESSION, REFERENCE_EXPRESSION -> importedFqName = FqName(it.asText)
|
||||
REFERENCE_EXPRESSION, DOT_QUALIFIED_EXPRESSION -> {
|
||||
importedFqName = mutableListOf<String>()
|
||||
.apply { collectSegments(it) }
|
||||
.joinToString(".")
|
||||
.let { FqName(it) }
|
||||
}
|
||||
MUL -> isAllUnder = true
|
||||
IMPORT_ALIAS -> {
|
||||
val importAlias = convertImportAlias(it)
|
||||
@@ -215,6 +220,18 @@ class DeclarationsConverter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<String>.collectSegments(expression: LighterASTNode) {
|
||||
when (expression.tokenType) {
|
||||
REFERENCE_EXPRESSION -> add(expression.asText)
|
||||
DOT_QUALIFIED_EXPRESSION -> {
|
||||
expression.forEachChildren {
|
||||
collectSegments(it)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseImportDirectives
|
||||
*/
|
||||
|
||||
+23
-8
@@ -13,9 +13,11 @@ import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildErrorImport
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildResolvedImport
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
import org.jetbrains.kotlin.fir.lookupTracker
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeImportFromSingleton
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedParentInImport
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
@@ -73,7 +75,13 @@ open class FirImportResolveTransformer protected constructor(
|
||||
get() = true
|
||||
|
||||
private fun transformImportForFqName(fqName: FqName, delegate: FirImport): FirImport {
|
||||
val (packageFqName, relativeClassFqName, classSymbol) = resolveToPackageOrClass(symbolProvider, fqName) ?: return delegate
|
||||
val (packageFqName, relativeClassFqName, classSymbol) = when (val result = resolveToPackageOrClass(symbolProvider, fqName)) {
|
||||
is PackageResolutionResult.Error -> return buildErrorImport {
|
||||
this.delegate = delegate
|
||||
this.diagnostic = result.diagnostic
|
||||
}
|
||||
is PackageResolutionResult.PackageOrClass -> result
|
||||
}
|
||||
val firClass = classSymbol?.fir as? FirRegularClass
|
||||
if (delegate.isAllUnder && firClass?.classKind?.isSingleton == true) {
|
||||
return buildErrorImport {
|
||||
@@ -89,7 +97,7 @@ open class FirImportResolveTransformer protected constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun resolveToPackageOrClass(symbolProvider: FirSymbolProvider, fqName: FqName): PackageOrClass? {
|
||||
fun resolveToPackageOrClass(symbolProvider: FirSymbolProvider, fqName: FqName): PackageResolutionResult {
|
||||
var currentPackage = fqName
|
||||
|
||||
val pathSegments = fqName.pathSegments()
|
||||
@@ -102,14 +110,21 @@ fun resolveToPackageOrClass(symbolProvider: FirSymbolProvider, fqName: FqName):
|
||||
prefixSize--
|
||||
}
|
||||
|
||||
if (currentPackage == fqName) return PackageOrClass(currentPackage, null, null)
|
||||
val relativeClassFqName =
|
||||
FqName.fromSegments((prefixSize until pathSegments.size).map { pathSegments[it].asString() })
|
||||
if (currentPackage == fqName) return PackageResolutionResult.PackageOrClass(currentPackage, null, null)
|
||||
val relativeClassFqName = FqName.fromSegments((prefixSize until pathSegments.size).map { pathSegments[it].asString() })
|
||||
|
||||
val classId = ClassId(currentPackage, relativeClassFqName, false)
|
||||
val symbol = symbolProvider.getClassLikeSymbolByClassId(classId) ?: return null
|
||||
val symbol = symbolProvider.getClassLikeSymbolByClassId(classId) ?: return PackageResolutionResult.Error(
|
||||
ConeUnresolvedParentInImport(classId)
|
||||
)
|
||||
|
||||
return PackageOrClass(currentPackage, relativeClassFqName, symbol)
|
||||
return PackageResolutionResult.PackageOrClass(currentPackage, relativeClassFqName, symbol)
|
||||
}
|
||||
|
||||
data class PackageOrClass(val packageFqName: FqName, val relativeClassFqName: FqName?, val classSymbol: FirClassLikeSymbol<*>?)
|
||||
sealed class PackageResolutionResult {
|
||||
data class PackageOrClass(
|
||||
val packageFqName: FqName, val relativeClassFqName: FqName?, val classSymbol: FirClassLikeSymbol<*>?
|
||||
) : PackageResolutionResult()
|
||||
|
||||
class Error(val diagnostic: ConeDiagnostic) : PackageResolutionResult()
|
||||
}
|
||||
|
||||
+1
-1
@@ -196,6 +196,6 @@ fun ImplicitReceiverValue<*>.asTowerDataElement(): FirTowerDataElement =
|
||||
fun FirScope.asTowerDataElement(isLocal: Boolean): FirTowerDataElement =
|
||||
FirTowerDataElement(this, implicitReceiver = null, isLocal)
|
||||
|
||||
private fun FirClass.staticScope(sessionHolder: SessionHolder) =
|
||||
fun FirClass.staticScope(sessionHolder: SessionHolder) =
|
||||
scopeProvider.getStaticScope(this, sessionHolder.session, sessionHolder.scopeSession)
|
||||
typealias FirLocalScopes = PersistentList<FirLocalScope>
|
||||
|
||||
+5
@@ -198,6 +198,11 @@ class ConeImportFromSingleton(val name: Name) : ConeDiagnostic {
|
||||
|
||||
open class ConeUnsupported(override val reason: String, val source: KtSourceElement? = null) : ConeDiagnostic
|
||||
|
||||
class ConeUnresolvedParentInImport(val parentClassId: ClassId) : ConeDiagnostic {
|
||||
override val reason: String
|
||||
get() = "unresolved import"
|
||||
}
|
||||
|
||||
class ConeUnsupportedDynamicType : ConeUnsupported("Dynamic types are not supported in this context")
|
||||
|
||||
class ConeDeprecated(
|
||||
|
||||
Reference in New Issue
Block a user