[FIR] Implement CONFLICTING_IMPORT diagnostic

This commit is contained in:
Andrey Zinovyev
2021-06-02 15:26:19 +03:00
committed by Space
parent 14789cb9e5
commit 7c669b65c3
18 changed files with 99 additions and 284 deletions
@@ -951,6 +951,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val CANNOT_BE_IMPORTED by error<KtSimpleNameExpression>(PositioningStrategy.IMPORT_LAST_NAME) {
parameter<Name>("name")
}
val CONFLICTING_IMPORT by error<KtImportDirective>(PositioningStrategy.IMPORT_LAST_NAME) {
parameter<Name>("name")
}
}
}
@@ -48,6 +48,7 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtIfExpression
import org.jetbrains.kotlin.psi.KtImportDirective
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtObjectDeclaration
@@ -522,5 +523,6 @@ object FirErrors {
val CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON by error1<KtSimpleNameExpression, Name>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
val PACKAGE_CANNOT_BE_IMPORTED by error0<KtSimpleNameExpression>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
val CANNOT_BE_IMPORTED by error1<KtSimpleNameExpression, Name>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
val CONFLICTING_IMPORT by error1<KtImportDirective, Name>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
}
@@ -6,11 +6,14 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.followAllAlias
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.name.ClassId
object FirImportsChecker : FirFileChecker() {
@@ -24,14 +27,14 @@ object FirImportsChecker : FirFileChecker() {
checkCanBeImported(import, context, reporter)
}
}
checkConflictingImports(declaration.imports, context, reporter)
}
private fun checkAllUnderFromEnumEntry(import: FirImport, context: CheckerContext, reporter: DiagnosticReporter) {
val fqName = import.importedFqName ?: return
if (fqName.isRoot || fqName.parent().isRoot) return
val classId = ClassId.topLevel(fqName.parent())
val classSymbol = context.session.symbolProvider.getClassLikeSymbolByFqName(classId) ?: return
val classFir = classSymbol.fir as? FirRegularClass ?: return
val classFir = classId.resolveToClass(context) ?: return
if (classFir.isEnumClass && classFir.collectEnumEntries().any { it.name == fqName.shortName() }) {
reporter.reportOn(import.source, FirErrors.CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON, classFir.name, context)
}
@@ -42,12 +45,12 @@ object FirImportsChecker : FirFileChecker() {
val importedName = importedFqName.shortName()
//empty name come from LT in some erroneous cases
if (importedName.isSpecial || importedName.identifier.isEmpty()) return
val classId = (import as? FirResolvedImport)?.resolvedClassId
if (classId != null) {
val classSymbol = context.session.symbolProvider.getClassLikeSymbolByFqName(classId) ?: return
val classFir = classSymbol.fir as? FirRegularClass ?: return
val classFir = classId.resolveToClass(context) ?: return
if (classFir.classKind.isSingleton) return
val illegalImport = classFir.declarations.any {
it is FirSimpleFunction && !it.isStatic && it.name == importedName ||
it is FirProperty && it.name == importedName
@@ -65,4 +68,48 @@ object FirImportsChecker : FirFileChecker() {
}
}
}
private fun checkConflictingImports(imports: List<FirImport>, context: CheckerContext, reporter: DiagnosticReporter) {
val interestingImports = imports
.filterIsInstance<FirResolvedImport>()
.filter { import ->
!import.isAllUnder
&& import.importedName?.identifierOrNullIfSpecial?.isNotEmpty() == true
&& import.resolvesToClass(context)
}
interestingImports
.groupBy { it.aliasName ?: it.importedName!! }
.values
.filter { it.size > 1 }
.forEach { conflicts ->
conflicts.forEach {
reporter.reportOn(it.source, FirErrors.CONFLICTING_IMPORT, it.importedName!!, context)
}
}
}
private fun FirResolvedImport.resolvesToClass(context: CheckerContext): Boolean {
if (resolvedClassId != null) {
if (isAllUnder) return true
val parentClass = resolvedClassId!!
val relativeClassName = this.relativeClassName ?: return false
val importedName = this.importedName ?: return false
val innerClassId = ClassId(parentClass.packageFqName, relativeClassName.child(importedName), false)
return innerClassId.resolveToClass(context) != null
} else {
val importedFqName = importedFqName ?: return false
if (importedFqName.isRoot) return false
val importedClassId = ClassId.topLevel(importedFqName)
return importedClassId.resolveToClass(context) != null
}
}
private fun ClassId.resolveToClass(context: CheckerContext): FirRegularClass? {
val classSymbol = context.session.symbolProvider.getClassLikeSymbolByFqName(this) ?: return null
return when (classSymbol) {
is FirRegularClassSymbol -> classSymbol.fir
is FirTypeAliasSymbol -> classSymbol.fir.followAllAlias(context.session) as? FirRegularClass
else -> null
}
}
}
@@ -70,6 +70,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTIO
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_ON_NULLABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONDITION_TYPE_MISMATCH
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_IMPORT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_OVERLOADS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_PROJECTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION
@@ -1174,6 +1175,11 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
"Cannot import ''{0}'', functions and properties can be imported only from packages or objects",
TO_STRING
)
map.put(
CONFLICTING_IMPORT,
"Conflicting import, imported name ''{0}'' is ambiguous",
TO_STRING
)
// Extended checkers group
map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier")