https://kotlinlang.org logo
d

dephinera

04/12/2020, 10:29 AM
Hello, everyone! Any references for getting started with Intellij plugins? More specifically for editing the PSI for code generation. Do you have anything that you've found very useful? I want to create a compiler plugin + IDE plugin for exposing mutable private properties as immutable public ones. For example:
Copy code
class A {
    @Expose
    private val mutableList = mutableListOf<String>()
}

class B(val a: A) {
    init {
        a.mutableList.add("hi") // ERROR!! the type here would be just List<String> instead of MutableList<String>
    }
}
I'm doing it as a project at my university, but I'll release it as open source. I want to put an end to the private mutable-pulbic immutable boilerplate.
I'm also wondering if arrow-meta would make the job easier in this case
r

raulraja

04/12/2020, 3:42 PM
The issue you have here is discerning between things that are read only or inmutable vs things that aren't. In the PSI phase you want all the type info but you'll be guessing based on names. This will change a bit when FIR offers compiler plugin extensions to it'd phases.
You can intercept analysis though after it's completed and bail with the message collector once all the expressions are typed and any of them returns what you consider mutable having access to all type information
Meta has both Cli analysis and means to build IDE counterparts where you reuse the code or simply call analyze in the Psi nodes you are interested
You can also build this without meta subscribing to the analysts phase and manually building an IDE plugin that does the same with highlithers or intentions
d

dephinera

04/13/2020, 6:51 AM
Thank you very much!!
👍 1
Hello @raulraja, I just started the project. Can you please clarify do I need to create a CliPlugin that will be used in Meta#intercept or everything I want to do can be achieved by overriding Meta#analysys? What I understand so far is I need to create my own implementation of AnalysisHandler and modify the tree in onAnalysisCompleted (where I'll have all the information about the property type, which properties are annotated with my annotation, etc.) - is that correct?
I suppose I have to iterate over the
files: Collection<KtFile>
argument, right? Does that mean that I'll be traversing the whole project? Is there a way I can optimize this by marking the properties I need during analysis and then when its completed to iterate and work only on them?
And because I see I wrote that in #intellij-plugins, I'm going for using meta for implementing the compiler plugin as well. That's the first thing I want to achieve and then I'll move to the IDE plugin, which hopefully will use the nearly same code
r

raulraja

06/09/2020, 7:49 PM
cc @Imran/Malic
i

Imran/Malic

06/09/2020, 8:40 PM
Hi @dephinera, regarding the Ide: We’re currently in the process to publish snapshots, so AFAIK the latest snapshot, which we published manually does not have the changes from this PR, where 3rd party plugins are enabled and extensions that are not, yet in Meta can be described in the Meta DSL- that was required to write example Plugins. If you want to get started right away, without waiting until that snapshot in master is available. One may fork Meta and create a new directory in
idea-plugin/src/main/kotlin/arrow/meta/ide/plugins/<plugin name>
for the ide plugin. One can do the same for the Cli Plugin. If that snapshot is available. I can check in with you and guide you through extracting your plugin to a standalone project with your compiler plugin. It will look like this. Only with the changes from master, which change a few types around, but the rough idea stays the same.
d

dephinera

06/09/2020, 8:49 PM
Thank you @Imran/Malic. For the time being I'm not concerned about the IDE plugin. My first priority is to make the code generation work for the compiler. What I'm struggling to understand is what are my options to modify the AST by generating a property based on another one's type and annotation. What I understand so far is that I actually can't modify the AST after the analysis is completed as it's immutable in that phase, no? Is replacing strings in an earlier phase my only option or is there another one? P.s: This is a university project so there is no problem using versions that are not merged into master.
i

Imran/Malic

06/09/2020, 9:00 PM
If you want to generate code the quote system might be a good place to start. There are a bunch of examples here.
There one can match on specific nodes in the AST and generate code. Correct me if I am wrong @raulraja, but in place changes won’t be picked up by the Ide, so the idea is to create synthetic descriptors, which the quote system supports. @bloder also build
Transform.newSources
to create new kotlin files, like kapt would do - only this is done at compile time with synthetic resolution for the generated descriptors in the Ide.
b

bloder

06/09/2020, 9:09 PM
Hi guys! The
Transform.newSources
is responsible for creating new kotlin files, but you also can combine different ast transformations using
TransformMany
as @Imran/Malic suggested.
i

Imran/Malic

06/09/2020, 9:23 PM
Thanks Daniel for clarifying. I’ll change that in my message rq.
👍 1
d

dephinera

06/09/2020, 9:27 PM
So to confirm understand correctly - I can use quote for code generation? I'll be generating a property in the same class. I'll need its information (its name, type, what it is annotated with and is it val or var). Do i have that kind of information in Quote? I thought I don't
b

bloder

06/09/2020, 11:22 PM
If I understood correctly what u want here is not a code "generation" but a code "transformation / replacement", you can use
Transform.replace
to create AST transformations and arrow-meta quotes provides a support for properties, with this support you can take a look what information property quote provides you and you can build your logic from that, I recommend to take a look on: https://github.com/arrow-kt/arrow-meta/blob/master/compiler-plugin/src/main/kotlin/arrow/meta/quotes/nameddeclaration/stub/typeparameterlistowner/Property.kt#L32 https://github.com/arrow-kt/arrow-meta/blob/a8339723d916418a33c0a95a7599003910e64b74/compiler-plugin/src/test/kotlin/arrow/meta/quotes/scope/plugins/PropertyPlugin.kt https://github.com/arrow-kt/arrow-meta/blob/a8339723d916418a33c0a95a7599003910e64b74/compiler-plugin/src/test/kotlin/arrow/meta/quotes/scope/templates/PropertyTest.kt
with that you can transform your private mutable properties into its immutable type and make it public just by intercepting your annotations and transforming its property AST and Quotes provides a more friendly api to do that.
d

dephinera

06/10/2020, 7:54 PM
Thanks @bloder! I don't know why I was left with the impression that when using Quotes you don't the information about the property (e.g what it is annotated with, what its type is, etc.). I'll give it a try.
r

raulraja

06/10/2020, 8:55 PM
You don't have the full typed descriptors because it's before analysis but you can Rewind analysis so quotes have types. We plan to implement that when someone finds some time as the current priority is finishing the arrow type system features so they are ready for 1.4
You can retry analysis if you return RetryWithAdditionalRoots as result of analysis completed.
But we should really implement typed quotes automatically in meta so it encapsulates the pattern
d

dephinera

06/11/2020, 6:12 AM
So it was what I thought after all. I'll give that a try, thanks @raulraja. Out of curiosity - how do you plan to implement that in meta? Would it be with rewinding again? Wouldn't that have some performance overhead?
r

raulraja

06/11/2020, 8:08 AM
It should not have noticeable overhead because by the time you rewind everything is analyzed already and you retain the binding trace and context. So only files modified in the rewind would be reanalized depending on the kinds of declarations you are generating.
d

dephinera

06/11/2020, 7:33 PM
Back to this again. So I tried returning
RetryWithAdditionalRoots
but I can see the compilers is passing through the phases only once. At least through the analysis and quotes phase. Could it be because I'm passing empty lists to
RetryWithAdditionalRoots
and because there is nothing new to analyze? Also what is the
addToEnvironment
Boolean parameter for?
What I also don't understand is why this code in analysisCompleted doesn't output anything:
Copy code
val slice = bindingTrace.bindingContext.getSliceContents(BindingContext.DECLARATION_TO_DESCRIPTOR)

                                slice.forEach {  (psi, descriptor) ->
                                    messageCollector?.report(
                                            CompilerMessageSeverity.WARNING,
                                            "${psi.text} -> Type: ${(descriptor as PropertyDescriptor).returnType}"
                                    )
                                }
I was going through this thread but I couldn't follow everything...
r

raulraja

06/11/2020, 9:49 PM
Yes you need to add new files for analysis RetryWithAdditionalRoots. I don't think anyone has gotten this far with quotes so if it works let us know and we can include the pattern in meta as an alternative to untyped quotes.
2 Views