FIR: preliminary implementation of diagnostics highlighter (~30% supported)

This commit is contained in:
Mikhail Glukhikh
2019-12-03 14:49:48 +03:00
parent 34202faaa5
commit ccb343e26b
10 changed files with 1126 additions and 35 deletions
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.FirProvider
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.diagnostics.collectors.FirDiagnosticsCollector
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDesignatedBodyResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.runResolve
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
@@ -101,17 +102,33 @@ fun KtClassOrObject.getOrBuildFir(
return firClass
}
private fun KtFile.getOrBuildRawFirFile(state: FirResolveState): Pair<IdeFirProvider, FirFile> {
val session = state.getSession(this)
val firProvider = FirProvider.getInstance(session) as IdeFirProvider
return firProvider to firProvider.getOrBuildFile(this)
}
fun KtFile.getOrBuildFir(
state: FirResolveState,
phase: FirResolvePhase = FirResolvePhase.DECLARATIONS
): FirFile {
val session = state.getSession(this)
val firProvider = FirProvider.getInstance(session) as IdeFirProvider
val firFile = firProvider.getOrBuildFile(this)
val (firProvider, firFile) = getOrBuildRawFirFile(state)
firFile.runResolve(firFile, firProvider, phase, state)
return firFile
}
fun KtFile.getOrBuildFirWithDiagnostics(state: FirResolveState): FirFile {
// TODO: consider adding some locks
val (_, firFile) = getOrBuildRawFirFile(state)
firFile.runResolve(toPhase = FirResolvePhase.BODY_RESOLVE, fromPhase = firFile.resolvePhase)
if (state.hasDiagnosticsForFile(this)) return firFile
val coneDiagnostics = FirDiagnosticsCollector.create().collectDiagnostics(firFile)
state.setDiagnosticsForFile(this, firFile, coneDiagnostics)
return firFile
}
private fun FirDeclaration.runResolve(
file: FirFile,
firProvider: IdeFirProvider,
@@ -120,33 +137,34 @@ private fun FirDeclaration.runResolve(
) {
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
file.runResolve(toPhase = nonLazyPhase, fromPhase = this.resolvePhase)
if (toPhase > nonLazyPhase) {
val designation = mutableListOf<FirElement>()
designation += file
if (this !is FirFile) {
val id = when (this) {
is FirCallableDeclaration<*> -> {
this.symbol.callableId.classId
}
is FirRegularClass -> {
this.symbol.classId
}
else -> error("Unsupported: ${render()}")
if (toPhase <= nonLazyPhase) return
val designation = mutableListOf<FirDeclaration>(file)
if (this !is FirFile) {
val id = when (this) {
is FirCallableDeclaration<*> -> {
this.symbol.callableId.classId
}
val outerClasses = generateSequence(id) { classId ->
classId.outerClassId
}.mapTo(mutableListOf()) { firProvider.getFirClassifierByFqName(it)!! }
designation += outerClasses.asReversed()
if (this is FirCallableDeclaration<*>) {
designation += this
is FirRegularClass -> {
this.symbol.classId
}
else -> error("Unsupported: ${render()}")
}
val outerClasses = generateSequence(id) { classId ->
classId.outerClassId
}.mapTo(mutableListOf()) { firProvider.getFirClassifierByFqName(it)!! }
designation += outerClasses.asReversed()
if (this is FirCallableDeclaration<*>) {
designation += this
}
val transformer = FirDesignatedBodyResolveTransformer(
designation.iterator(), state.getSession(psi as KtElement),
implicitTypeOnly = toPhase == FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE
)
file.transform<FirFile, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
}
if (designation.all { it.resolvePhase >= toPhase }) {
return
}
val transformer = FirDesignatedBodyResolveTransformer(
designation.iterator(), state.getSession(psi as KtElement),
implicitTypeOnly = toPhase == FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE
)
file.transform<FirFile, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
}
fun KtElement.getOrBuildFir(
@@ -6,18 +6,19 @@
package org.jetbrains.kotlin.idea.fir
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionProvider
import org.jetbrains.kotlin.fir.dependenciesWithoutSelf
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.java.FirLibrarySession
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.idea.caches.project.isLibraryClasses
import org.jetbrains.kotlin.idea.caches.resolve.IDEPackagePartProvider
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.addToStdlib.cast
private fun createLibrarySession(moduleInfo: IdeaModuleInfo, project: Project, provider: FirProjectSessionProvider): FirLibrarySession {
@@ -58,17 +59,51 @@ interface FirResolveState {
operator fun get(psi: KtElement): FirElement?
fun getDiagnostics(psi: KtElement): List<Diagnostic>
fun hasDiagnosticsForFile(file: KtFile): Boolean
fun record(psi: KtElement, fir: FirElement)
fun record(psi: KtElement, diagnostic: Diagnostic)
fun setDiagnosticsForFile(file: KtFile, fir: FirFile, diagnostics: Iterable<ConeDiagnostic>)
}
class FirResolveStateImpl(override val sessionProvider: FirSessionProvider) : FirResolveState {
private val cache = mutableMapOf<KtElement, FirElement>()
private val diagnosticCache = mutableMapOf<KtElement, MutableList<Diagnostic>>()
private val diagnosedFiles = mutableSetOf<KtFile>()
override fun get(psi: KtElement): FirElement? = cache[psi]
override fun getDiagnostics(psi: KtElement): List<Diagnostic> {
return diagnosticCache[psi] ?: emptyList()
}
override fun hasDiagnosticsForFile(file: KtFile): Boolean {
return file in diagnosedFiles
}
override fun record(psi: KtElement, fir: FirElement) {
cache[psi] = fir
}
override fun record(psi: KtElement, diagnostic: Diagnostic) {
// TODO: consider implementing custom FirIdeDiagnosticReported/Collector
val list = diagnosticCache.getOrPut(psi) { mutableListOf() }
list += diagnostic
}
override fun setDiagnosticsForFile(file: KtFile, fir: FirFile, diagnostics: Iterable<ConeDiagnostic>) {
for (diagnostic in diagnostics) {
(diagnostic.source.psi as? KtElement)?.let { record(it, diagnostic.diagnostic) }
}
diagnosedFiles += file
}
}
fun KtElement.firResolveState(): FirResolveState =
@@ -43,13 +43,13 @@ import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
import org.jetbrains.kotlin.idea.fir.FirResolution
import org.jetbrains.kotlin.idea.fir.firResolveState
import org.jetbrains.kotlin.idea.fir.getOrBuildFirWithDiagnostics
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
import org.jetbrains.kotlin.idea.quickfix.QuickFixes
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.types.KotlinType
@@ -63,7 +63,19 @@ open class KotlinPsiChecker : Annotator, HighlightRangeExtension {
if (!KotlinHighlightingUtil.shouldHighlight(file)) return
val analysisResult = file.analyzeWithAllCompilerChecks()
if (FirResolution.enabled) {
annotateElementUsingFrontendIR(element, file, holder)
} else {
annotateElement(element, file, holder)
}
}
private fun annotateElement(
element: PsiElement,
containingFile: KtFile,
holder: AnnotationHolder
) {
val analysisResult = containingFile.analyzeWithAllCompilerChecks()
if (analysisResult.isError()) {
throw ProcessCanceledException(analysisResult.error)
}
@@ -75,6 +87,25 @@ open class KotlinPsiChecker : Annotator, HighlightRangeExtension {
annotateElement(element, holder, bindingContext.diagnostics)
}
private fun annotateElementUsingFrontendIR(
element: PsiElement,
containingFile: KtFile,
holder: AnnotationHolder
) {
if (element !is KtElement) return
val state = containingFile.firResolveState()
containingFile.getOrBuildFirWithDiagnostics(state)
val diagnostics = state.getDiagnostics(element)
if (diagnostics.isEmpty()) return
if (KotlinHighlightingUtil.shouldHighlightErrors(element)) {
ElementAnnotator(element, holder) { param ->
shouldSuppressUnusedParameter(param)
}.registerDiagnosticsAnnotations(diagnostics)
}
}
override fun isForceHighlightParents(file: PsiFile): Boolean {
return file is KtFile
}