Kotlin constructor reference in Intellij - kotlin

It is not really a Kotlin question, maybe an Intellij question, I don't know.
Assume we have a data class
data class Person(val name: String = "untitled", val age: Int = 20)
And we have a function
fun factory(cstr: ()->Person) : Person {
return cstr()
}
Then we can invoke factory(::Person) and obtain an instance of Person class with default constructor parameters.
The fun factory can be invoked successfully wherever. But in IntelliJ I get a red underline error
Look like the IDE failed to recognize that there is a default constructor.
However, If I change the code like that, the error goes away. Everything runs perfectly and no error is shown in the IDE.
I am using IntelliJ 2020.2 and Kotlin 1.4.10.
Maybe it is about some IntelliJ inspection rules, but I cannot find one related.
Further, it is a piece of old code that showed no error before (maybe 5 months ago). I am not sure what has bee change since then caused the error.
So the problem is why is Intellij show error for lambda version and not for KFunction version?

I assume invoking factory like this works fine:
factory(() -> Person())
Then it seems your version of the Kotlin IDE plugin does not handle constructor references well in this case. The compiler does, since the code works. If the IDE plugin version is the latest, please file a bug at https://youtrack.jetbrains.com/issues/KT

I reproduced the problem with old inference enabled in the build.gradle file: freeCompilerArgs += ["-XXLanguage:-NewInference"]. Please make sure you're using new (default) type inference in Kotlin 1.4+. So, remove the compiler argument, that should fix the IDE highlighting after Gradle project reimport into IDEA.

Related

How can I stop IntelliJ IDEA from turning methods into properties when Kotlinizing a Java file?

In IntelliJ IDEA I can right-click on a Java file and convert it to Kotlin.
It mostly goes well, but this irks me: When the method name starts with is, in Kotlin it comes out as a property instead of a function.
Is there a setting I can change to get Kotlin to convert these methods to functions like all the other methods?
As of Kotlin IDEA plugin 1.4.0, every getter-like method that starts with get or is is always converted into a Kotlin property. This behavior cannot be overridden.
The problem is described in the issue https://youtrack.jetbrains.com/issue/KT-36826, you can vote for it and discuss further on YouTrack.

Gradle. Custom function in block plugins{}

Can i write in my custom plugin some function like kotlin("jvm")?
plugins {
java
kotlin("jvm") version "1.3.71"
}
I want to write function myplugin("foo") in my custom plugin and then use it like
plugins {
java
kotlin("jvm") version "1.3.71"
custom.plugin
myplugin("foo")
}
How i can do it?
I think that plugins block is some kind of a macro expression. It is parsed and precompiled using a very limited context. Probably, the magic happens somewhere in kotlin-dsl. This is probably the only way to get static accessors and extension functions from plugins to work in Kotlin. I've never seen a mention of this process in Gradle's documentation, but let me explain my thought. Probably, some smart guys from Gradle will correct me.
Let's take a look at some third-party plugin, like Liquibase. It allows you to write something like this in your build.gradle.kts:
liquibase {
activities {
register("name") {
// Configure the activity here
}
}
}
Think about it: in a statically compiled language like Kotlin, in order for this syntaxt to work, there should be an extension named liquibase on a Project type (as it is the type of this object in every build.gradle.kts) available in the classpath of a Gradle's VM that executes the build script.
Indeed, if you click on it, you'll see something like:
fun org.gradle.api.Project.`liquibase`(configure: org.liquibase.gradle.LiquibaseExtension.() -> Unit): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("liquibase", configure)
But take a look at the file where it is defined. In my case it is ~/.gradle/caches/6.3/gradle-kotlin-dsl-accessors/cmljl3ridzazieb8fzn553oa8/cache/src/org/gradle/kotlin/dsl/Accessors39qcxru7gldpadn6lvh8lqs7b.kt. It is definitelly an auto-generated file. A few levels upper in a file tree — at ~/.gradle/caches/6.3/gradle-kotlin-dsl-accessors/ in my case — there are dozens of similar directories. I guess, one by every plugin/version I've ever used with Gradle 6.3. Here is another one for the Detekt plugin:
fun org.gradle.api.Project.`detekt`(configure: io.gitlab.arturbosch.detekt.extensions.DetektExtension.() -> Unit): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("detekt", configure)
So, we have a bunch of .kt files defining all that extensions for different plugins applied to the project. That files are obviously pre-cached and precompiled and their content is available in build.gradle.kts. Indeed, you can find classes directories beside those sources.
The sources are generated based on the content of the applied plugins. It is probably a tricky task that includes some magic, reflection and introspection. Sometimes this magic doesn't work (due too chatic Groovy nature) and then you need to use some crappy DSL from this package.
How are they generated? I see no other way, but to
Parse the build.script.kts with an embedded Kotlin compiler / lexer
Extract all the plugins sections
Compile them, probably against some mocks (remember that Project is not yet available: we're not executing the build.gradle.kts itself yet!)
Resolve the declared plugins from Gradle Plugin repository (with some nuances coming from settngs.gradle.kts)
Introspect plugin's artifacts
Generate the sources
Compile the sources
Add the resulting classes to the script's classpath
And here is the gotcha: there is a very limited context (classpath, classes, methods — call it whatever) available when compiling the plugins block. Actually, no plugins are yet applied! Because, you know, you're parsing the block that applies plugins. Chickens, eggs, and their problems, huh…
So, and we're getting closer to the answer on your question, to provide custom DSL in plugins block, you need to modify that classpath. It's not a classpath of your build.gradle.kts, it's the classpath of the VM that parses build.gradle.kts. Basically, it's Gradle's own classpath — all the classes bundled in a Gradle distribution.
So, probably the only way to provide really custom DSLs in plugins block is to create a custom Gradle distribution.
EDIT:
Indeed, totally forgot to test the buildSrc. I've created a file PluginExtensions.kt in it, with a content
inline val org.gradle.plugin.use.PluginDependenciesSpec.`jawa`: org.gradle.plugin.use.PluginDependencySpec
get() = id("org.gradle.war") // Randomly picked
inline fun org.gradle.plugin.use.PluginDependenciesSpec.`jawa`(): org.gradle.plugin.use.PluginDependencySpec {
return id("org.gradle.cunit") // Randomly picked
}
And it seems to be working:
plugins {
jawa
jawa()
}
However, this is only working when PluginExtensions.kt is in the default package. Whenever I put it into a sub-package, the extensions are not recognized, even with an import:
Magic!
The kotlin function is just a simple extension function wrapping the traditional id method, not hard to define:
fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec =
id("org.jetbrains.kotlin.$module")
However, this extension function is part of the standard gradle kotlin DSL API, which means it's available without any plugin. If you want to make a custom function like this available, you would need a plugin. A plugin to load your plugin. Not very practical.
I also tried using the buildSrc module to make an extension function like the above. But it turns out that buildSrc definitions aren't even available from the plugins DSL block, which has a very constrained syntax. That wouldn't have been very practical anyway, you would have needed to make a buildSrc folder for every project in which you have wanted to use the extension.
I'm not sure if this is possible at all. Try asking on https://discuss.gradle.org/.

Using GroovyDSL with #TypeChecked in IntelliJ IDEA: Build Project fails

I have a jenkins.gdsl file defining some bindings I'm using in my Groovy script. In addition, I'd like to use the #TypeChecked annotation on my methods to get some guarantees about built code.
My jenkins.gdsl file looks like:
contributor(context(scope: scriptScope())) {
// some definitions
}
And then my script.groovy looks like:
#TypeChecked(extensions='jenkins.gdsl')
void doStuff() {
// ...
}
IntelliJ IDEA autocomplete works, but when building my project I get an error in my jenkins.gdsl file:
Error:Groovyc: groovy.lang.MissingMethodException: No signature of method: org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport.scriptScope() is applicable for argument types: () values: []
Removing (extensions='jenkins.gdsl') gets rid of this error, but then I lose my GDSL definitions when building, so that's a no-go.
It feels like the solution would involve bringing in IntelliJ's standardDsls. I am not at all sure how to do this, or whether it is in fact the correct approach.
#TypeChecked is a Groovy compiler annotation that can run some code during compilation.
But gdsl is an IntelliJ IDEA-specific script that's used only by the IDE to provide some completion and other coding assistance. It doesn't have anything in common with the compiler, and neither of those know anything of each other. So you can remove the extensions value, as it won't provide any typechecking during compilation.

Calling java code that doesn't accept null from kotlin

IDEA Community 2017.1.2, JRE 1.8, Kotlin 1.1.2-2
I have Java methods, located in libGdx that don't have any annotations regarding their nullability, e.g.:
public void render (final RenderableProvider renderableProvider) {
renderableProvider.getRenderables(renderables, renderablesPool);
as we can see, argument can't be null. However, since nothing tells that it's not-null argument, Kotlin will happily pass null in RenderableProvider?. How do I tell Kotlin to check during compile-time that I should be passing RenderableProvider and not RenderableProvider??
I've read about external annotations, however there is no "Specify Custom Kotlin Signature" and if I annotate renderableProvider as #NotNull nothing changes - kotlin still allows null.
I even tried to replace org.jetbrains.annotations.NotNull with javax.annotation.Nonnull in XML manually, but it makes no difference - code compiles and crashes with NPE.
External annotations are no longer supported. You'll either have to fork libgdx and annotate the methods there or live with this issue, unfortunately.
You could wrap it in an extension function and then only use that for rendering:
fun RenderClass.renderSafe(renderProvider: RenderableProvider) =
this.render(renderProvider)
Now you can't pass null.

Generated classes not found

Using Dagger2 and Kotlin, my component class implementations are not being generated when other calling code exists. When no calling code exists, the implementations are generated.
E.g.
val comp = DaggerMyComponent.create()
Causes the build to fail, DaggerMyComponent is not generated and thus cannot be found
// val comp = DaggerMyComponent.create()
The DaggerMyComponent class is generate and can be viewed on disk, the build succeeds.
I've tried Dagger 2.0.1, 2.0.2, 2.1-Snapshot, using Kotlin beta-1103.
I can post my gradle file, source code, or stack trace if needed.
Has anyone encountered this issue before?
In your app's build file, make sure you added
kapt {
generateStubs = true
}
some example projects can be found here
https://github.com/damianpetla/kotlin-dagger-example/tree/master/app
https://github.com/burntcookie90/KotlinDaggerDataBinding