55
66package org.jetbrains.kotlinx.dataframe.plugin.extensions
77
8+ import com.intellij.psi.PsiElement
9+ import org.jetbrains.kotlin.KtSourceElement
10+ import org.jetbrains.kotlin.diagnostics.AbstractSourceElementPositioningStrategy
11+ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1DelegateProvider
812import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
13+ import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1
14+ import org.jetbrains.kotlin.diagnostics.Severity
915import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
1016import org.jetbrains.kotlin.diagnostics.error1
1117import org.jetbrains.kotlin.diagnostics.reportOn
1218import org.jetbrains.kotlin.diagnostics.warning1
1319import org.jetbrains.kotlin.fir.FirSession
1420import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
1521import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
22+ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
23+ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirPropertyChecker
24+ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirSimpleFunctionChecker
1625import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
1726import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
27+ import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirPropertyAccessExpressionChecker
1828import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
1929import org.jetbrains.kotlin.fir.caches.FirCache
20- import org.jetbrains.kotlinx.dataframe.plugin.impl.api.flatten
21- import org.jetbrains.kotlinx.dataframe.plugin.pluginDataFrameSchema
30+ import org.jetbrains.kotlin.fir.declarations.FirProperty
31+ import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
2232import org.jetbrains.kotlin.fir.declarations.hasAnnotation
2333import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
34+ import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
2435import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
2536import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
2637import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
2738import org.jetbrains.kotlin.fir.types.ConeClassLikeType
39+ import org.jetbrains.kotlin.fir.types.ConeKotlinType
2840import org.jetbrains.kotlin.fir.types.FirTypeProjectionWithVariance
2941import org.jetbrains.kotlin.fir.types.coneType
3042import org.jetbrains.kotlin.fir.types.isSubtypeOf
@@ -38,18 +50,34 @@ import org.jetbrains.kotlin.name.FqName
3850import org.jetbrains.kotlin.name.Name
3951import org.jetbrains.kotlin.psi.KtElement
4052import org.jetbrains.kotlinx.dataframe.plugin.impl.PluginDataFrameSchema
53+ import org.jetbrains.kotlinx.dataframe.plugin.impl.SimpleColumnGroup
4154import org.jetbrains.kotlinx.dataframe.plugin.impl.SimpleDataColumn
42- import org.jetbrains.kotlinx.dataframe.plugin.impl.type
55+ import org.jetbrains.kotlinx.dataframe.plugin.impl.SimpleFrameColumn
56+ import org.jetbrains.kotlinx.dataframe.plugin.impl.api.flatten
57+ import org.jetbrains.kotlinx.dataframe.plugin.pluginDataFrameSchema
4358import org.jetbrains.kotlinx.dataframe.plugin.utils.Names
59+ import org.jetbrains.kotlinx.dataframe.plugin.utils.isDataFrame
60+ import org.jetbrains.kotlinx.dataframe.plugin.utils.isDataRow
61+ import org.jetbrains.kotlinx.dataframe.plugin.utils.isGroupBy
4462
4563class ExpressionAnalysisAdditionalChecker (
4664 session : FirSession ,
4765 cache : FirCache <String , PluginDataFrameSchema , KotlinTypeFacade >,
4866 schemasDirectory : String? ,
4967 isTest : Boolean ,
68+ dumpSchemas : Boolean
5069) : FirAdditionalCheckersExtension(session) {
5170 override val expressionCheckers: ExpressionCheckers = object : ExpressionCheckers () {
52- override val functionCallCheckers: Set <FirFunctionCallChecker > = setOf (Checker (cache, schemasDirectory, isTest))
71+ override val functionCallCheckers: Set <FirFunctionCallChecker > = setOfNotNull(
72+ Checker (cache, schemasDirectory, isTest), FunctionCallSchemaReporter .takeIf { dumpSchemas }
73+ )
74+ override val propertyAccessExpressionCheckers: Set <FirPropertyAccessExpressionChecker > = setOfNotNull(
75+ PropertyAccessSchemaReporter .takeIf { dumpSchemas }
76+ )
77+ }
78+ override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers () {
79+ override val propertyCheckers: Set <FirPropertyChecker > = setOfNotNull(PropertySchemaReporter .takeIf { dumpSchemas })
80+ override val simpleFunctionCheckers: Set <FirSimpleFunctionChecker > = setOfNotNull(FunctionDeclarationSchemaReporter .takeIf { dumpSchemas })
5381 }
5482}
5583
@@ -132,3 +160,105 @@ private class Checker(
132160 }
133161 }
134162}
163+
164+ private data object PropertySchemaReporter : FirPropertyChecker (mppKind = MppCheckerKind .Common ) {
165+ val SCHEMA by info1<KtElement , String >(SourceElementPositioningStrategies .DECLARATION_NAME )
166+
167+ override fun check (declaration : FirProperty , context : CheckerContext , reporter : DiagnosticReporter ) {
168+ context.sessionContext {
169+ declaration.returnTypeRef.coneType.let { type ->
170+ reportSchema(reporter, declaration.source, SCHEMA , type, context)
171+ }
172+ }
173+ }
174+ }
175+
176+ private data object FunctionCallSchemaReporter : FirFunctionCallChecker (mppKind = MppCheckerKind .Common ) {
177+ val SCHEMA by info1<KtElement , String >(SourceElementPositioningStrategies .REFERENCED_NAME_BY_QUALIFIED )
178+
179+ override fun check (expression : FirFunctionCall , context : CheckerContext , reporter : DiagnosticReporter ) {
180+ if (expression.calleeReference.name in setOf (Name .identifier(" let" ), Name .identifier(" run" ))) return
181+ val initializer = expression.resolvedType
182+ context.sessionContext {
183+ reportSchema(reporter, expression.source, SCHEMA , initializer, context)
184+ }
185+ }
186+ }
187+
188+ private data object PropertyAccessSchemaReporter : FirPropertyAccessExpressionChecker (mppKind = MppCheckerKind .Common ) {
189+ val SCHEMA by info1<KtElement , String >(SourceElementPositioningStrategies .REFERENCED_NAME_BY_QUALIFIED )
190+
191+ override fun check (
192+ expression : FirPropertyAccessExpression ,
193+ context : CheckerContext ,
194+ reporter : DiagnosticReporter
195+ ) {
196+ val initializer = expression.resolvedType
197+ context.sessionContext {
198+ reportSchema(reporter, expression.source, SCHEMA , initializer, context)
199+ }
200+ }
201+ }
202+
203+ private data object FunctionDeclarationSchemaReporter : FirSimpleFunctionChecker (mppKind = MppCheckerKind .Common ) {
204+ val SCHEMA by info1<KtElement , String >(SourceElementPositioningStrategies .DECLARATION_SIGNATURE )
205+
206+ override fun check (declaration : FirSimpleFunction , context : CheckerContext , reporter : DiagnosticReporter ) {
207+ val type = declaration.returnTypeRef.coneType
208+ context.sessionContext {
209+ reportSchema(reporter, declaration.source, SCHEMA , type, context)
210+ }
211+ }
212+ }
213+
214+ private fun SessionContext.reportSchema (
215+ reporter : DiagnosticReporter ,
216+ source : KtSourceElement ? ,
217+ factory : KtDiagnosticFactory1 <String >,
218+ type : ConeKotlinType ,
219+ context : CheckerContext ,
220+ ) {
221+ val expandedType = type.fullyExpandedType(session)
222+ var schema: PluginDataFrameSchema ? = null
223+ when {
224+ expandedType.isDataFrame(session) -> {
225+ schema = expandedType.typeArguments.getOrNull(0 )?.let {
226+ pluginDataFrameSchema(it)
227+ }
228+ }
229+
230+ expandedType.isDataRow(session) -> {
231+ schema = expandedType.typeArguments.getOrNull(0 )?.let {
232+ pluginDataFrameSchema(it)
233+ }
234+ }
235+
236+ expandedType.isGroupBy(session) -> {
237+ val keys = expandedType.typeArguments.getOrNull(0 )
238+ val grouped = expandedType.typeArguments.getOrNull(1 )
239+ if (keys != null && grouped != null ) {
240+ val keysSchema = pluginDataFrameSchema(keys)
241+ val groupedSchema = pluginDataFrameSchema(grouped)
242+ schema = PluginDataFrameSchema (
243+ listOf (
244+ SimpleColumnGroup (" keys" , keysSchema.columns()),
245+ SimpleFrameColumn (" groups" , groupedSchema.columns())
246+ )
247+ )
248+ }
249+ }
250+ }
251+ if (schema != null && source != null ) {
252+ reporter.reportOn(source, factory, " \n " + schema.toString(), context)
253+ }
254+ }
255+
256+ fun CheckerContext.sessionContext (f : SessionContext .() -> Unit ) {
257+ SessionContext (session).f()
258+ }
259+
260+ inline fun <reified P : PsiElement , A > info1 (
261+ positioningStrategy : AbstractSourceElementPositioningStrategy = SourceElementPositioningStrategies .DEFAULT
262+ ): DiagnosticFactory1DelegateProvider <A > {
263+ return DiagnosticFactory1DelegateProvider (Severity .INFO , positioningStrategy, P ::class )
264+ }
0 commit comments