Checking if a UMethod is an instance of KtFunction breaks IDE integration

326 views
Skip to first unread message

Dani Vila Teissiere

unread,
May 23, 2018, 3:13:36 AM5/23/18
to lint-dev
Hello,

Thank you for this great tool, I've been using it for almost a year now :) recently, I've been facing this problem when writing a custom rule, could it be a bug or maybe we are not supposed to check use instance of when visiting a method? Taking the example method I change it to the following:

/**
* Sample detector showing how to analyze Kotlin/Java code.
* This example flags all string literals in the code that contain
* the word "lint".
*/
public class SampleCodeDetector extends Detector implements UastScanner {
/**
* Issue describing the problem and pointing to the detector implementation
*/
public static final Issue ISSUE = Issue.create(
// ID: used in @SuppressLint warnings etc
"ShortUniqueId",

// Title -- shown in the IDE's preference dialog, as category headers in the
// Analysis results window, etc
"Lint Mentions",

// Full explanation of the issue; you can use some markdown markup such as
// `monospace`, *italic*, and **bold**.
"This check highlights string literals in code which mentions " +
"the word `lint`. Blah blah blah.\n" +
"\n" +
"Another paragraph here.\n",
Category.CORRECTNESS,
6,
Severity.WARNING,
new Implementation(
SampleCodeDetector.class,
Scope.JAVA_FILE_SCOPE));

@Override
public List<Class<? extends UElement>> getApplicableUastTypes() {
return Collections.singletonList(UMethod.class);
}

@Override
public UElementHandler createUastHandler(JavaContext context) {
// Not: Visiting UAST nodes is a pretty general purpose mechanism;
// Lint has specialized support to do common things like "visit every class
// that extends a given super class or implements a given interface", and
// "visit every call site that calls a method by a given name" etc.
// Take a careful look at UastScanner and the various existing lint check
// implementations before doing things the "hard way".
// Also be aware of context.getJavaEvaluator() which provides a lot of
// utility functionality.
return new UElementHandler() {
@Override
public void visitMethod(UMethod node) {
// boolean isKotlinFunction = node instanceof KotlinUMethod;
context.report(ISSUE, node, context.getNameLocation(node),
"This is a method");
}
};
}
}



This rule is quite straight forward, it just highlights every method, and it works well with Android Studio. But as soon as I uncomment
\\boolean isKotlinFunction = node instanceof KtFunction; The report in Android Studio stops working. Seems like it breaks when I check KotlinUMethod but if I check JavaUMethod it doesn't break.

I'm using Android Studio 3.1.2(MacOS), Lint version: 26.1.2 and Gradle Plugin Version: 3.1.2. 

Thank you!

Regards,

Daniel.

Tor Norbye

unread,
Jul 5, 2018, 11:51:34 AM7/5/18
to lint-dev
In general, you should avoid touching the language-specific plugins for UAST (JavaU*, KotlinU*).
It's an accident that the UAST java plugin is available on the classpath. The Kotlin UAST plugin is only available if you depend on the "CLI" module for lint (the command line runner used by the batch script, the gradle integration etc) which needs it to configure the environment, but you typically should not directly depend on it.

What is it you're trying to do? If you're just trying to see if you're in Kotlin (or not in Kotlin), you can call the method isKotlin(element) (it's a package function; from Java call Lint.isKotlin(element)".

NOTE: As of 3.3 (not yet visible in the first canary published last week, but in an upcoming build), the above will change, and client code will be able to access the Kotlin PSI directly. We're doing that for a number of reasons, including making it easier to do Kotlin-specific analysis and refactoring/quickfixes, which isn't possible via UAST.

-- Tor


Niklas Baudy

unread,
Jul 10, 2018, 7:14:47 AM7/10/18
to lint-dev
Interesting. Didn't know about not using JavaU and KotlinU types. Somewhere back in my head I remember that you actually wanted to write lint checks that check custom lint rules since there are many things that can go wrong. I think it was an Android Developers Backstage episode - but don't quote me. There are a few things that one always need to think of which could easily be wrapped into custom lint rules:

- Don't read the file directly instead use the context.client.readFile function
- Use the evaluator when possible e.g. isMemberInClass, getAllAnnotations and all the other methods
- java("""code""").intended() instead of trimMargin with | for proper syntax highlighting
- Implementing the corresponding matching detector scope interface e.g. EnumSet.of(GRADLE_FILE) with a Detector that does not implement Detector.GradleScanner won't work
- Touching JavaU* and KotlinU* classes

Also I feel like getting the custom lint checks to work from the CI is very easy since they just work. However if you want them to be shown in the IDE too you can screw things up pretty easily. The big problem is that we can't even see why it's failing. Can we get some debugging functionality for that?

There's probably a ton more that only very few people know.

Dani Vila Teissiere

unread,
Jul 13, 2018, 3:41:34 AM7/13/18
to lint-dev
I'm trying to know if the kotlin method has a return type explicitly defined:

fun intFunc() = 10
fun intFunc(): Int = 10

So I thought casting the UMethod to KtNamedFunction and checking if the colon attribute is null would be enough:

val namedFunc = node.sourcePsiElement as? KtNamedFunction

if (namedFunc != null && namedFunc.colon == null) {
context.run {
report(
ISSUE, node, getNameLocation(node),
reportMessage(node.name)
)
}
}

Regards,

Daniel.
Reply all
Reply to author
Forward
0 new messages