5
5
6
6
package org.jetbrains.kotlinx.dataframe.plugin.extensions
7
7
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
8
12
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
13
+ import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1
14
+ import org.jetbrains.kotlin.diagnostics.Severity
9
15
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
10
16
import org.jetbrains.kotlin.diagnostics.error1
11
17
import org.jetbrains.kotlin.diagnostics.reportOn
12
18
import org.jetbrains.kotlin.diagnostics.warning1
13
19
import org.jetbrains.kotlin.fir.FirSession
14
20
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
15
21
import 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
16
25
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
17
26
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
27
+ import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirPropertyAccessExpressionChecker
18
28
import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
19
29
import 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
22
32
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
23
33
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
34
+ import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
24
35
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
25
36
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
26
37
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
27
38
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
39
+ import org.jetbrains.kotlin.fir.types.ConeKotlinType
28
40
import org.jetbrains.kotlin.fir.types.FirTypeProjectionWithVariance
29
41
import org.jetbrains.kotlin.fir.types.coneType
30
42
import org.jetbrains.kotlin.fir.types.isSubtypeOf
@@ -38,18 +50,34 @@ import org.jetbrains.kotlin.name.FqName
38
50
import org.jetbrains.kotlin.name.Name
39
51
import org.jetbrains.kotlin.psi.KtElement
40
52
import org.jetbrains.kotlinx.dataframe.plugin.impl.PluginDataFrameSchema
53
+ import org.jetbrains.kotlinx.dataframe.plugin.impl.SimpleColumnGroup
41
54
import 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
43
58
import 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
44
62
45
63
class ExpressionAnalysisAdditionalChecker (
46
64
session : FirSession ,
47
65
cache : FirCache <String , PluginDataFrameSchema , KotlinTypeFacade >,
48
66
schemasDirectory : String? ,
49
67
isTest : Boolean ,
68
+ dumpSchemas : Boolean
50
69
) : FirAdditionalCheckersExtension(session) {
51
70
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 })
53
81
}
54
82
}
55
83
@@ -132,3 +160,105 @@ private class Checker(
132
160
}
133
161
}
134
162
}
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