Suppress DeprecationLevel.ERROR in Kotlin - kotlin

I’m using DeprecationLevel.ERROR in my APIs:
#Deprecated(
message = "moved to def()",
replaceWith = ReplaceWith(expression = "def()"),
level = DeprecationLevel.ERROR)
fun abc() = def()
I want a test to ensure callers see this replacement. For example, my test should fail to compile if I accidentally delete the abc() method.
But I can’t find a way to make this compile:
#Test
#Suppress("something")
fun deprecatedAbc() {
abc()
}
For example, #Suppress("DEPRECATION") doesn’t work. Is there something that does?

According to DefaultErrorMessages, you can use #Suppress("DEPRECATION_ERROR").

According to the documentation of #Deprecated:
To help removing deprecated API gradually, the property level could be used. Usually a gradual phase-out goes through the "warning", then "error", then "hidden" or "removed" stages:
First and by default, DeprecationLevel.WARNING is used to notify API consumers, but not to break their compilation or runtime usages.
Then, some time later the deprecation level is raised to DeprecationLevel.ERROR, so that no new Kotlin code can be compiled using the deprecated API.
Code does not compile by design -- and #Suppress only suppresses warnings, not errors (see doc).
So, what you are trying to achieve is not possible. Also, it seems to me as if you're testing a Kotlin feature, rather than your business logic. You might instead try to test the behavior of the replacement.

Related

How can I tell the Kotlin compiler that a Java method will never return null?

I don't or can't modify the Java source code. The goal to configure just the Kotlin compiler to know what is nullable and what isn't.
You can specify the type manually if you know something will never be null. For example, if you have the following Java code:
public static Foo test() {
return null;
}
and you call it in Kotlin like this:
val result = Foo.test()
then result will have a type of Foo! by default – which means it can be either Foo or Foo?.. the compiler doesn't have enough information to determine that.
However, you can force the type manually:
val result: Foo = Foo.test()
// use "result" as a non-nullable type
Of course, if at runtime that is not true, you'll get a NullPointerException.
For reference, please check the documentation.
I don't know of a way to configure the compiler for this, but IntelliJ IDEA has a feature that allows you to add annotations to code via an XML file called external annotations.
You can add the Jetbrains #Nullable and #NotNull annotations to library code, but when I've tried it, it only results in compiler warnings rather than errors when you use incorrect nullability in your code. These same annotations generate compiler errors when used directly in the source code. I don't know why there is a difference in behavior.
You can use extension functions for this. If you have a method String foo() in the class Test, you can define the extension function
fun Test.safeFoo(): String = this.foo()!!
The advantage is that the code is pretty obious.
The disadvantage of this approach is that you need to write a lot of boiler plate code. You also have to define the extension function in a place where all your modules or projects can see it. Also, writing that much code just to avoid !! feels like overkill.
It should also be possible to write a Kotlin compiler extension which generates them for you but the extension would need to know which methods never return null.

How can one invoke the non-extension `run` function (the one without scope / "object reference") in environments where there is an object scope?

Example:
data class T(val flag: Boolean) {
constructor(n: Int) : this(run {
// Some computation here...
<Boolean result>
})
}
In this example, the custom constructor needs to run some computation in order to determine which value to pass to the primary constructor, but the compiler does not accept the run, citing Cannot access 'run' before superclass constructor has been called, which, if I understand correctly, means instead of interpreting it as the non-extension run (the variant with no object reference in https://kotlinlang.org/docs/reference/scope-functions.html#function-selection), it construes it as a call to this.run (the variant with an object reference in the above table) - which is invalid as the object has not completely instantiated yet.
What can I do in order to let the compiler know I mean the run function which is not an extension method and doesn't take a scope?
Clarification: I am interested in an answer to the question as asked, not in a workaround.
I can think of several workarounds - ways to rewrite this code in a way that works as intended without calling run: extracting the code to a function; rewriting it as a (possibly highly nested) let expression; removing the run and invoking the lambda (with () after it) instead (funnily enough, IntelliJ IDEA tags that as Redundant lambda creation and suggests to Inline the body, which reinstates the non-compiling run). But the question is not how to rewrite this without using run - it's how to make run work in this context.
A good answer should do one of the following things:
Explain how to instruct the compiler to call a function rather than an extension method when a name is overloaded, in general; or
Explain how to do that specifically for run; or
Explain that (and ideally also why) it is not possible to do (ideally with supporting references); or
Explain what I got wrong, in case I got something wrong and the whole question is irrelevant (e.g. if my analysis is incorrect, and the problem is something other than the compiler construing the call to run as this.run).
If someone has a neat workaround not mentioned above they're welcome to post it in a comment - not as an answer.
In case it matters: I'm using multi-platform Kotlin 1.4.20.
Kotlin favors the receiver overload if it is in scope. The solution is to use the fully qualified name of the non-receiver function:
kotlin.run { //...
The specification is explained here.
Another option when the overloads are not in the same package is to use import renaming, but that won't work in this case since both run functions are in the same package.

What's the real intention behind Kotlins also scope function

I'm asking myself what the language designers intention behind the also scope function was and if almost everyone is misusing it.
If you search here on stack overflow for examples of Kotlins scope functions, you'll end up with this accepted answer: https://stackoverflow.com/a/45977254/5122729
The given answer for also { } is
also - use it when you want to use apply, but don't want to shadow
this
class FruitBasket {
private var weight = 0
fun addFrom(appleTree: AppleTree) {
val apple = appleTree.pick().also { apple ->
this.weight += apple.weight
add(apple)
}
...
}
...
fun add(fruit: Fruit) = ... }
Using apply here would shadow this, so that this.weight would refer to
the apple, and not to the fruit basket.
That's also the usage I see quite often. But if I have a look into the documentation at kotlinlang.org, they are clearly saying:
also is good for performing some actions that take the context object
as an argument. Use also for additional actions that don't alter the
object, such as logging or printing debug information. Usually, you
can remove the calls of also from the call chain without breaking the
program logic.
From that point of view, the given example would be wrong as it would break the program logic if it is removed. For me, also is kind of Javas peek (doc), which is there, but should not be used for productive program logic.
Can someone enlighten me?
After having a longer discussion on Reddit about this topic, the documentation was adjusted in a way were the sentence
Usually, you can remove the calls of also from the call chain without
breaking the program logic.
was removed. See the corresponding PR: https://github.com/JetBrains/kotlin-web-site/pull/1676

JacksonSerializer doesn't work in Kotlin Lambda

When I create a client with the JacksonSerializer() feature and make some API calls, then run that script on my local machine, I get no error and the script runs successfully. However, when I upload this script as an AWS Lambda, I get the following error:
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of kotlin.coroutines.Continuation, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
At first, I thought the error originated from me constructing the client outside of the Handler class, but when I made the client a private value inside the Handler class, I still get the error. I've included println() statements in my function, but they don't even run. That tells me that my handleRequest() funciton isn't getting run. Is there some AWS/Lambda'ism that prevents me from using the JacksonSerializer() feature as? If so, are there any alternatives on how to parse JSON responses with the Ktor client?
My client construction:
private val client = HttpClient(Apache) {
install(JsonFeature) {
serializer = JacksonSerializer()
}
}
An example call using the client:
val response = client.post<JsonNode> {
url(URL(GITHUB_GRAPHQL_ENDPOINT))
body = reqBody
headers {
append("Authorization", "bearer $token")
}
}
I'm guessing that you made your handler function be a kotlin suspend function? If so, that's your problem.
When you mark a function suspend, the compiler applies a bunch of magic. Most of the time, you don't need to know anything about this, other than the fact that any suspend function gets an extra parameter of type kotlin.coroutines.Continuation added to its signature. You usually don't notice this, since the compiler also makes calls to the function pass along their own hidden Continuation parameter.
Continuation, by design, can't be created by a tool like Jackson - it's an internal thing. What you probably need to do (assuming that you did make your handler function suspend) is to wrap your method in runBlocking {} and make it not be a suspend function. It's probably easiest to create a new handler, like so:
fun fixedHandler(input: MyInput, context: Context) = runBlocking {
originalHandler(input, context)
}
suspend fun originalHandler(input: MyInput, context: Context): MyOutput {
TODO("This is your original code")
}
PS - I've usually found it best to leverage the pre-defined Lambda interfaces to write my Lambda functions - it prevents you from encountering issues like this. See https://docs.aws.amazon.com/lambda/latest/dg/java-handler-using-predefined-interfaces.html for how to do it.
Have you checked out your dependencies running locally versus your dependencies in AWS? I've had issues where locally I'm running with a version, but the version in AWS was different. That could especially explain the error about continuations... Perhaps the method signature is different in whatever version you're using?
Look especially for provided scopes in your gradle/maven pom. Those are easy places for version to be out of sync.

Jinq in Kotlin - how to convert lambda into java SerializedLambda?

Can I have serializable lambda in Kotlin? I am trying to use Jinq library from Kotlin, but it requires serializable lambdas. Is there any syntax that makes it possible?
Update:
My code:
var temp=anyDao.streamAll(Task::class.java)
.where<Exception,Task> { t->t.taskStatus== TaskStatus.accepted }
.collect(Collectors.toList<Task>());
I am getting this error:
Caused by: java.lang.IllegalArgumentException:
Could not extract code from lambda.
This error sometimes occurs because your lambda references objects that aren't Serializable.
All objects referenced in lambda are serializable (code results in no errors in java).
Update 2
After debugging it seems that kotlin lambda isn't translated into java.lang.invoke.SerializedLambda which is required by Jinq to get information from. So the problem is how to convert it to SerializedLambda.
I'm the maker of Jinq. I haven't had the time to look at Kotlin-support, but based on your description, I'm assuming that Kotlin compiles its lambdas into actual classes or something else. As such, Jinq would probably need some special code for cracking open Kotlin lambdas, and it may also need special code for handling any unusual Kotlin-isms in the generated code. Jinq should be capable of handling it because it was previously retrofitted to handle Scala lambdas.
If you file an issue in the Jinq github about it, along with a small Kotlin example (in both source and .class file form), then I can take a quick peek at what might be involved. If it's small, I can make those changes. Unfortunately, if it looks like a lot of work, I don't think I can really justify putting a lot of resources into adding Kotlin support to Jinq.
I have no experience on Jinq, but according to the implementation in GitHub and my experience of using Java Library in Kotlin.
ref: https://github.com/my2iu/Jinq/blob/master/api/src/org/jinq/orm/stream/JinqStream.java
You can always fall back to use the native Java Interface in Kotlin.
var temp = anyDao.streamAll(Task::class.java)
.where( JinqStream.Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
// Alternatively, You you can import the interface first
import org.jinq.orm.stream.JinqStream.*
...
// then you can use Where instead of JinqStream.Where
var temp = anyDao.streamAll(Task::class.java)
.where(Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
Or make a custom extension to wrap the implementation
fun JinqStream<T>.where(f: (T) -> Boolean): JinqStream<T> {
return this.where(JinqStream.Where<T,Exception> { f(it) })
}
Disclaimer: The above codes have not been tested.