FIR LT: add syntax error reporting to LT2Fir

use it in the new pipeline
This commit is contained in:
Ilya Chernikov
2021-12-29 22:08:21 +01:00
committed by teamcity
parent 2689ebf455
commit 9e32188938
21 changed files with 98 additions and 40 deletions
@@ -69,6 +69,7 @@ object FirDiagnosticToKtDiagnosticConverterRenderer : AbstractDiagnosticsDataCla
override val defaultImports = listOf(
"org.jetbrains.kotlin.diagnostics.KtPsiDiagnostic",
"org.jetbrains.kotlin.fir.builder.FirSyntaxErrors",
"org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors",
"org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors",
)
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.KtPsiSourceElement
import org.jetbrains.kotlin.diagnostics.KtPsiDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
@@ -93,12 +94,6 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.SYNTAX) { firDiagnostic ->
SyntaxImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.OTHER_ERROR) { firDiagnostic ->
OtherErrorImpl(
firDiagnostic as KtPsiDiagnostic,
@@ -102,10 +102,6 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val error: String
}
abstract class Syntax : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = Syntax::class
}
abstract class OtherError : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = OtherError::class
}
@@ -105,11 +105,6 @@ internal class NewInferenceErrorImpl(
override val token: ValidityToken,
) : KtFirDiagnostic.NewInferenceError(), KtAbstractFirDiagnostic<PsiElement>
internal class SyntaxImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.Syntax(), KtAbstractFirDiagnostic<PsiElement>
internal class OtherErrorImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: ValidityToken,
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.CodegenFactory
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.DependencyListForCliModule
import org.jetbrains.kotlin.fir.FirSession
@@ -123,6 +124,7 @@ fun compileModulesUsingFrontendIrAndLightTree(
compilerEnvironment,
emptyList(),
null,
diagnosticsReporter
)
// TODO: consider what to do if many modules has main classes
if (mainClassFqName == null && moduleConfiguration.get(JVMConfigurationKeys.OUTPUT_JAR) != null) {
@@ -224,6 +226,7 @@ fun compileModuleToAnalyzedFir(
environment: ModuleCompilerEnvironment,
previousStepsSymbolProviders: List<FirSymbolProvider>,
incrementalExcludesScope: AbstractProjectFileSearchScope?,
diagnosticsReporter: DiagnosticReporter
): ModuleCompilerAnalyzedOutput {
var sourcesScope = environment.projectEnvironment.getSearchScopeByIoFiles(input.platformSources) //!!
val sessionProvider = FirProjectSessionProvider()
@@ -270,8 +273,8 @@ fun compileModuleToAnalyzedFir(
}
// raw fir
val commonRawFir = commonSession?.buildFirViaLightTree(input.commonSources)
val rawFir = session.buildFirViaLightTree(input.platformSources)
val commonRawFir = commonSession?.buildFirViaLightTree(input.commonSources, diagnosticsReporter)
val rawFir = session.buildFirViaLightTree(input.platformSources, diagnosticsReporter)
// resolution
commonSession?.apply {
+1
View File
@@ -8,6 +8,7 @@ plugins {
dependencies {
api(project(":compiler:fir:providers"))
api(project(":compiler:fir:semantics"))
api(project(":compiler:fir:raw-fir:raw-fir.common"))
implementation(project(":compiler:frontend.common"))
implementation(project(":compiler:frontend.common-psi"))
implementation(project(":compiler:psi"))
@@ -52,7 +52,6 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
}
val Miscellaneous by object : DiagnosticGroup("Miscellaneous") {
val SYNTAX by error<PsiElement>()
val OTHER_ERROR by error<PsiElement>()
}
@@ -111,7 +111,6 @@ object FirErrors {
val NEW_INFERENCE_ERROR by error1<PsiElement, String>()
// Miscellaneous
val SYNTAX by error0<PsiElement>()
val OTHER_ERROR by error0<PsiElement>()
// General syntax
@@ -473,7 +473,6 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_NOT_INI
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_CALL_FROM_PUBLIC_INLINE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_IS_NOT_AN_EXPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_NOT_AVAILABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SYNTAX
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TAILREC_ON_VIRTUAL_MEMBER_ERROR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.THROWABLE_TYPE_MISMATCH
@@ -586,7 +585,6 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(NEW_INFERENCE_ERROR, "New inference error [{0}]", STRING)
// Miscellaneous
map.put(SYNTAX, "Syntax error")
map.put(OTHER_ERROR, "Unknown (other) error")
// General syntax
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
import org.jetbrains.kotlin.fir.analysis.getChild
import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors
import org.jetbrains.kotlin.fir.declarations.utils.isInfix
import org.jetbrains.kotlin.fir.declarations.utils.isOperator
import org.jetbrains.kotlin.fir.diagnostics.*
@@ -386,7 +387,7 @@ private val NewConstraintError.upperConeType: ConeKotlinType get() = upperType a
private fun ConeSimpleDiagnostic.getFactory(source: KtSourceElement): KtDiagnosticFactory0 {
@Suppress("UNCHECKED_CAST")
return when (kind) {
DiagnosticKind.Syntax -> FirErrors.SYNTAX
DiagnosticKind.Syntax -> FirSyntaxErrors.SYNTAX
DiagnosticKind.ReturnNotAllowed -> FirErrors.RETURN_NOT_ALLOWED
DiagnosticKind.NotAFunctionLabel -> FirErrors.NOT_A_FUNCTION_LABEL
DiagnosticKind.UnresolvedLabel -> FirErrors.UNRESOLVED_LABEL
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.pipeline
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
@@ -16,10 +17,10 @@ import org.jetbrains.kotlin.fir.session.sourcesToPathsMapper
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
fun FirSession.buildFirViaLightTree(files: Collection<File>): List<FirFile> {
fun FirSession.buildFirViaLightTree(files: Collection<File>, diagnosticsReporter: DiagnosticReporter? = null): List<FirFile> {
val firProvider = (firProvider as FirProviderImpl)
val sourcesToPathsMapper = sourcesToPathsMapper
val builder = LightTree2Fir(this, firProvider.kotlinScopeProvider)
val builder = LightTree2Fir(this, firProvider.kotlinScopeProvider, diagnosticsReporter)
return files.map {
builder.buildFirFile(it).also { firFile ->
firProvider.recordFile(firFile)
@@ -17,9 +17,9 @@ import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.cli.common.toBooleanLenient
import org.jetbrains.kotlin.cli.jvm.compiler.*
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.analysis.collectors.FirDiagnosticsCollector
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.declarations.FirFile
@@ -149,7 +149,7 @@ class FirResolveModularizedTotalKotlinTest : AbstractModularizedTest() {
val firProvider = session.firProvider as FirProviderImpl
val firFiles = if (USE_LIGHT_TREE) {
val lightTree2Fir = LightTree2Fir(session, firProvider.kotlinScopeProvider)
val lightTree2Fir = LightTree2Fir(session, firProvider.kotlinScopeProvider, diagnosticsReporter = null)
val allSourceFiles = moduleData.sources.flatMap {
if (it.isDirectory) {
@@ -10,8 +10,13 @@ import com.intellij.lang.impl.PsiBuilderFactoryImpl
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticContext
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.lightTree.converter.DeclarationsConverter
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.lexer.KotlinLexer
@@ -22,7 +27,8 @@ import java.nio.file.Path
class LightTree2Fir(
val session: FirSession,
private val scopeProvider: FirScopeProvider
private val scopeProvider: FirScopeProvider,
private val diagnosticsReporter: DiagnosticReporter? = null
) {
companion object {
private val parserDefinition = KotlinParserDefinition()
@@ -66,9 +72,18 @@ class LightTree2Fir(
fun buildFirFile(code: String, fileName: String, path: String?): FirFile {
val lightTree = buildLightTree(code)
return DeclarationsConverter(session, scopeProvider, lightTree)
.convertFile(lightTree.root, fileName, path)
return DeclarationsConverter(
session, scopeProvider, lightTree, diagnosticsReporter = diagnosticsReporter,
diagnosticContext = makeDiagnosticContext(path)
).convertFile(lightTree.root, fileName, path)
}
private fun makeDiagnosticContext(path: String?) =
if (diagnosticsReporter == null) null else object : DiagnosticContext {
override val containingFilePath = path
override val languageVersionSettings: LanguageVersionSettings get() = session.languageVersionSettings
override fun isDiagnosticSuppressed(diagnostic: KtDiagnostic): Boolean = false
}
}
data class LightTreeFile(
@@ -10,9 +10,8 @@ import com.intellij.openapi.util.Ref
import com.intellij.psi.TokenType
import com.intellij.psi.tree.IElementType
import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.ElementTypeUtils.isExpression
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.ElementTypeUtils.isExpression
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.builder.BaseFirBuilder
import org.jetbrains.kotlin.fir.builder.Context
@@ -32,6 +31,8 @@ abstract class BaseConverter(
protected val implicitType = buildImplicitTypeRef()
protected open fun reportSyntaxError(node: LighterASTNode) {}
override fun LighterASTNode.toFirSourceElement(kind: KtFakeSourceElementKind?): KtLightSourceElement {
val startOffset = offset + tree.getStartOffset(this)
val endOffset = offset + tree.getEndOffset(this)
@@ -183,6 +184,10 @@ abstract class BaseConverter(
if (kid == null) break
val tokenType = kid.tokenType
if (COMMENTS.contains(tokenType) || tokenType == WHITE_SPACE || tokenType == SEMICOLON || tokenType in skipTokens) continue
if (tokenType == TokenType.ERROR_ELEMENT) {
reportSyntaxError(kid)
continue
}
f(kid)
}
}
@@ -195,6 +200,10 @@ abstract class BaseConverter(
if (kid == null) break
val tokenType = kid.tokenType
if (COMMENTS.contains(tokenType) || tokenType == WHITE_SPACE || tokenType == SEMICOLON) continue
if (tokenType == TokenType.ERROR_ELEMENT) {
reportSyntaxError(kid)
continue
}
f(kid, container)
}
@@ -19,6 +19,9 @@ import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*
import org.jetbrains.kotlin.diagnostics.DiagnosticContext
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.builder.*
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
@@ -59,7 +62,9 @@ class DeclarationsConverter(
private val baseScopeProvider: FirScopeProvider,
tree: FlyweightCapableTreeStructure<LighterASTNode>,
@set:PrivateForInline override var offset: Int = 0,
context: Context<LighterASTNode> = Context()
context: Context<LighterASTNode> = Context(),
private val diagnosticsReporter: DiagnosticReporter? = null,
private val diagnosticContext: DiagnosticContext? = null
) : BaseConverter(session, tree, context) {
@OptIn(PrivateForInline::class)
@@ -75,6 +80,10 @@ class DeclarationsConverter(
private val expressionConverter = ExpressionsConverter(session, tree, this, context)
override fun reportSyntaxError(node: LighterASTNode) {
diagnosticsReporter?.reportOn(node.toFirSourceElement(), FirSyntaxErrors.SYNTAX, diagnosticContext!!)
}
/**
* [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile]
* [org.jetbrains.kotlin.parsing.KotlinParsing.parsePreamble]
@@ -1694,7 +1703,8 @@ class DeclarationsConverter(
val blockTree = LightTree2Fir.buildLightTreeBlockExpression(block.asText)
return DeclarationsConverter(
baseSession, baseScopeProvider, blockTree, offset = offset + tree.getStartOffset(block), context
baseSession, baseScopeProvider, blockTree, offset = offset + tree.getStartOffset(block), context,
diagnosticsReporter, diagnosticContext
).convertBlockExpression(blockTree.root)
}
@@ -20,7 +20,8 @@ abstract class AbstractLightTree2FirConverterTestCase : AbstractRawFirBuilderTes
fun doTest(filePath: String) {
val firFile = LightTree2Fir(
session = FirSessionFactory.createEmptySession(),
scopeProvider = StubFirScopeProvider
scopeProvider = StubFirScopeProvider,
diagnosticsReporter = null
).buildFirFile(Paths.get(filePath))
val firDump = firFile.render(mode = FirRenderer.RenderMode.WithDeclarationAttributes)
@@ -50,7 +50,8 @@ class TotalKotlinTest : AbstractRawFirBuilderTestCase() {
val lightTreeConverter = LightTree2Fir(
session = FirSessionFactory.createEmptySession(),
scopeProvider = StubFirScopeProvider
scopeProvider = StubFirScopeProvider,
diagnosticsReporter = null
)
if (onlyLightTree) println("LightTree generation") else println("Fir from LightTree converter")
@@ -57,7 +57,8 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
private fun compareAll() {
val lightTreeConverter = LightTree2Fir(
session = FirSessionFactory.createEmptySession(),
scopeProvider = StubFirScopeProvider
scopeProvider = StubFirScopeProvider,
diagnosticsReporter = null
)
compareBase(System.getProperty("user.dir"), withTestData = false) { file ->
val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
@@ -78,7 +79,8 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
fun testCompareDiagnostics() {
val lightTreeConverter = LightTree2Fir(
session = FirSessionFactory.createEmptySession(),
scopeProvider = StubFirScopeProvider
scopeProvider = StubFirScopeProvider,
diagnosticsReporter = null
)
compareBase("compiler/testData/diagnostics/tests", withTestData = true) { file ->
if (file.name.endsWith(".fir.kt")) {
@@ -0,0 +1,29 @@
/*
* 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.builder
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.error0
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
object FirSyntaxErrors {
val SYNTAX by error0<PsiElement>()
init {
RootDiagnosticRendererFactory.registerFactory(FirSyntaxErrorsDefaultMessages)
}
}
@Suppress("unused")
object FirSyntaxErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
override val MAP = KtDiagnosticFactoryToRendererMap("FIR").also { map ->
map.put(FirSyntaxErrors.SYNTAX, "Syntax error")
}
}
@@ -211,6 +211,7 @@ class IncrementalFirJvmCompilerRunner(
compilerEnvironment,
emptyList(),
incrementalExcludesScope,
diagnosticsReporter
)
// TODO: consider what to do if many compilations find a main class
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
@@ -90,7 +91,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
)
) return@flatMap emptyList()
// SYNTAX errors will be reported later
if (diagnostic.factory == FirErrors.SYNTAX) return@flatMap emptyList()
if (diagnostic.factory == FirSyntaxErrors.SYNTAX) return@flatMap emptyList()
if (!diagnostic.isValid) return@flatMap emptyList()
diagnostic.toMetaInfos(
file,
@@ -114,7 +115,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
) {
val metaInfos = if (firFile.psi != null) {
AnalyzingUtils.getSyntaxErrorRanges(firFile.psi!!).flatMap {
FirErrors.SYNTAX.on(KtRealPsiSourceElement(it), positioningStrategy = null)
FirSyntaxErrors.SYNTAX.on(KtRealPsiSourceElement(it), positioningStrategy = null)
.toMetaInfos(
testFile,
globalMetadataInfoHandler1 = globalMetadataInfoHandler,
@@ -124,7 +125,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
}
} else {
collectLightTreeSyntaxErrors(firFile).flatMap { sourceElement ->
FirErrors.SYNTAX.on(sourceElement, positioningStrategy = null)
FirSyntaxErrors.SYNTAX.on(sourceElement, positioningStrategy = null)
.toMetaInfos(
testFile,
globalMetadataInfoHandler1 = globalMetadataInfoHandler,