Slide 1

Slide 1 text

After Kotlin Fest 2024 LT Night @Sansan ΋ͬͱ΋ͬͱKotlinΛ޷͖ʹͳΔʂ K2 Compiler PluginͰ༡ΜͰΈΑ͏ @kitakkun_pb

Slide 2

Slide 2 text

2 ొஃऀϓϩϑΟʔϧ

Slide 3

Slide 3 text

ࠓճ࿩͢͜ͱ

Slide 4

Slide 4 text

FIRνΣοΧʔ֦ுʂʂ

Slide 5

Slide 5 text

ηογϣϯΛݟ͍ͯͳ͍ํ΋ ͍Βͬ͠ΌΔͱࢥ͏ͷͰ

Slide 6

Slide 6 text

1෼ͰΘ͔Δ Kotlin Compiler Plugin ͷલʹʂ

Slide 7

Slide 7 text

1෼ͰΘ͔Δ Kotlin Compiler ͋͋͋ P l u g i n

Slide 8

Slide 8 text

Kotlin Compiler 8 ղੳ ࠷దԽɾग़ྗ Frontend Backend ιʔεϓϩάϥϜ ໨తίʔυ .class 0110 1010 JS JVM Native JS

Slide 9

Slide 9 text

தؒදݱʢIR: Intermediate Representationʣ தؒදݱ 
 ɹ= ίϯύΠϧதͷϓϩάϥϜΛ
 ɹ໦ߏ଄Ͱදݱͨ͠΋ͷ • Frontend → FIR • Backend → IR 9 IrClass IrSimpleFunction IrProperty IrExpressionBody IrBlockBody IrConst IrStatement IrStatement

Slide 10

Slide 10 text

1෼ͰΘ͔Δ Kotlin Compiler Plugin

Slide 11

Slide 11 text

Kotlin Compiler Plugin 11 Frontend Backend ιʔεϓϩάϥϜ ໨తίʔυ .class 0110 1010 JS JVM Native JS

Slide 12

Slide 12 text

Kotlin Compiler Plugin 12 Frontend Backend ιʔεϓϩάϥϜ ໨తίʔυ .class 0110 1010 JS JVM Native JS FIR֦ு IR֦ு FIRνΣοΧʔ

Slide 13

Slide 13 text

FIRνΣοΧʔͰ༡ΜͰΈΑ͏ʂ

Slide 14

Slide 14 text

copy ͸ private ίϯετϥΫλΛ͢Γൈ͚ΔʢBefore 2.0.20-Beta1ʣ 14 data class A private constructor(val a: Int) { companion object { fun create(a: Int) = A(a) } } val a = A.create(10) / / OK val b = a.copy(20) / / OK, but should be an error. ERROR❌

Slide 15

Slide 15 text

5छྨͷFIR Checker • એݴ FirDeclarationChecker • ࣜ FirExpressionChecker • ܕ FirTypeChecker • ݴޠόʔδϣϯ FirLanguageVersionSettingsChecker • ੍ޚϑϩʔ FirControlFlowChecker 15 ίϯύΠϥ֦ு
 Մೳͳ΋ͷ

Slide 16

Slide 16 text

5छྨͷFIR Checker • એݴ FirDeclarationChecker • ࣜ FirExpressionChecker • ܕ FirTypeChecker • ݴޠόʔδϣϯ FirLanguageVersionSettingsChecker • ੍ޚϑϩʔ FirControlFlowChecker 16 ίϯύΠϥ֦ு
 Մೳͳ΋ͷ

Slide 17

Slide 17 text

copy Λݕࠪ͢Δ FirFunctionCallChecker 17 object NoCopyFirFunctionCallChecker : FirFunctionCallChecker(MppCheckerKind.Common) { }

Slide 18

Slide 18 text

copy Λݕࠪ͢Δ FirFunctionCallChecker 18 object NoCopyFirFunctionCallChecker : FirFunctionCallChecker(…) { override fun check( expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter, ) { / / TODO: privateίϯετϥΫλͷΫϥεͷcopy࢖༻Λݕग़ } }

Slide 19

Slide 19 text

copy Λݕࠪ͢Δ FirFunctionCallChecker 19 override fun check( expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter, ) { // TODO1: copyҎ֎ͷؔ਺ݺͼग़͠Λআ֎ // TODO2: data class Ҏ֎Λআ֎ // TODO3: constructor ͕ private Ͱͳ͍΋ͷΛআ֎ // TODO4: copy ͷ࢖༻ՕॴΛΤϥʔใࠂ }

Slide 20

Slide 20 text

20 override fun check( expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter, ) { // TODO1: copyҎ֎ͷؔ਺ݺͼग़͠Λআ֎ if (expression.calleeReference.name != Name.identifier("copy")) { return } // . .. }

Slide 21

Slide 21 text

21 override fun check( expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter, ) { // copyҎ֎ͷؔ਺ݺͼग़͠Λআ֎ ✅ // TODO2: data class Ҏ֎Λআ֎ val classSymbol = expression.resolvedType .toRegularClassSymbol(context.session) ? : return if (!classSymbol.isData) return // .. . }

Slide 22

Slide 22 text

22 override fun check( expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter, ) { // copyҎ֎ͷؔ਺ݺͼग़͠Λআ֎ ✅ // data class Ҏ֎Λআ֎ ✅ // TODO3: constructor ͕ private Ͱͳ͍΋ͷΛআ֎ val constructorVisibility = classSymbol .primaryConstructorSymbol(context.session) .visibility ? : return if (constructorVisibility ! = Visibilities.Private) return // … }

Slide 23

Slide 23 text

23 override fun check( expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter, ) { // copyҎ֎ͷؔ਺ݺͼग़͠Λআ֎ ✅ // data class Ҏ֎Λআ֎ ✅ // constructor ͕ private Ͱͳ͍΋ͷΛআ֎ ✅ // TODO4: copy ͷ࢖༻ՕॴΛΤϥʔใࠂ }

Slide 24

Slide 24 text

24 override fun check( expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter, ) { // TODO4: copyͷ࢖༻ՕॴΛΤϥʔใࠂ reporter.reportOn( context = context, source = TODO("Ͳͷએݴʹର͢Δ΋ͷʁ"), factory = TODO("ରԠ͢ΔΤϥʔఆٛ͸ʁ"), a = TODO("Τϥʔใࠂ༻ͷ෇Ճ৘ใ") ) }

Slide 25

Slide 25 text

Diagnostic Reporter ͷΞʔΩςΫνϟ 25 DiagnosticRendererFactory ʓʓErrors DiagnosticReporter CheckerContext ਍அ৘ใͷఆٛ ਍அ৘ใͷ
 දࣔํ๏Λܾఆ νΣοΧʔ͕ѻ͏
 શͯͷ৘ใΛอ࣋ ਍அ৘ใͷใࠂ

Slide 26

Slide 26 text

ʓʓErrorsͷ࣮૷ʢΤϥʔ৘ใͷఆٛʣ 26 object NoCopyFirErrors { val COPY_CALL_FOR_DATA_CLASS_WITH_PRIVATE_CONSTRUCTOR by error1() init { RootDiagnosticRendererFactory.registerFactory( NoCopyErrorMessages ) } }

Slide 27

Slide 27 text

ʓʓErrorsͷ࣮૷ʢΤϥʔ৘ใͷఆٛʣ 27 object NoCopyFirErrors { val COPY_CALL_AGAINST_PRIVATE_CONSTRUCTOR_DATA_CLASS by error1() init { RootDiagnosticRendererFactory.registerFactory( NoCopyErrorMessages ) } }

Slide 28

Slide 28 text

DiagnosticRendererFactoryͷ࣮૷ʢΤϥʔͷදࣔํ๏ͷܾఆʣ 28 object NoCopyErrorMessages : BaseDiagnosticRendererFactory() { override val MAP = KtDiagnosticFactoryToRendererMap("NoCopy").apply { put( NoCopyFirErrors.COPY_CALL_FOR_DATA_CLASS_WITH_PRIVATE_CONSTRUCTOR, "data class {0} has a private constructor." + "The use of copy method is not allowed", rendererA = TO_STRING, ) } }

Slide 29

Slide 29 text

29 override fun check( expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter, ) { // TODO4: copyͷ࢖༻ՕॴΛΤϥʔใࠂ reporter.reportOn( context = context, source = TODO("Ͳͷએݴʹର͢Δ΋ͷʁ"), factory = TODO("ରԠ͢ΔΤϥʔఆٛ͸ʁ"), a = TODO("Τϥʔใࠂ༻ͷ෇Ճ৘ใ") ) }

Slide 30

Slide 30 text

30 / / TODO4: copyͷ࢖༻ՕॴΛΤϥʔใࠂ reporter.reportOn( context = context, source = TODO("Ͳͷએݴʹର͢Δ΋ͷʁ"), factory = TODO("ରԠ͢ΔΤϥʔఆٛ͸ʁ"), a = TODO("Τϥʔใࠂ༻ͷ෇Ճ৘ใ") )

Slide 31

Slide 31 text

31 / / TODO4: copyͷ࢖༻ՕॴΛΤϥʔใࠂ reporter.reportOn( context = context, source = expression.calleeReference.source, factory = TODO("ରԠ͢ΔΤϥʔఆٛ͸ʁ"), a = TODO("Τϥʔใࠂ༻ͷ෇Ճ৘ใ") )

Slide 32

Slide 32 text

32 // TODO4: copyͷ࢖༻ՕॴΛΤϥʔใࠂ reporter.reportOn( context = context, source = expression.calleeReference.source, factory = NoCopyFirErrors. COPY_CALL_FOR_DATA_CLASS_WITH_PRIVATE_CONSTRUCTOR, a = TODO("Τϥʔใࠂ༻ͷ෇Ճ৘ใ") )

Slide 33

Slide 33 text

33 // TODO4: copyͷ࢖༻ՕॴΛΤϥʔใࠂ reporter.reportOn( context = context, source = expression.calleeReference.source, factory = NoCopyFirErrors. COPY_CALL_FOR_DATA_CLASS_WITH_PRIVATE_CONSTRUCTOR, a = classSymbol.classId.asString() )

Slide 34

Slide 34 text

Checkerͷ࣮૷͸͓͠·͍ʂ

Slide 35

Slide 35 text

ࡉ͔͍࢒Γͷ࣮૷͸ ׂѪ͠·ͯ͠ɾɾ

Slide 36

Slide 36 text

ಈ࡞σϞΛ͓ݟͤ͠·͢

Slide 37

Slide 37 text

37

Slide 38

Slide 38 text

ࢀߟจݙ • νΣοΧʔΛ௥Ճ͢ΔFIR֦ு • https://github.com/JetBrains/kotlin/blob/master/docs/ fi r/ fi r- plugins.md# fi radditionalcheckersextension • FIRνΣοΧʔͷࡉ͔͍࢓༷ • https://github.com/JetBrains/kotlin/blob/master/compiler/ fi r/checkers/ module.md 38

Slide 39

Slide 39 text

ࢿྉɾαϯϓϧϦϙδτϦڞ༗ https://github.com/kitakkun/NoCopy-Compiler-Plugin

Slide 40

Slide 40 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ