Check whenever UField implements given interface

780 views
Skip to first unread message

przem...@gmail.com

unread,
Dec 30, 2017, 4:44:31 PM12/30/17
to lint-dev
Hey 

Firstly I am quite noob in writing custom lint rules. I have read tutorials, and also watched some youtube videos (including KotlinConf 2017) about creating custom rules. 

I have been struggling with writing rule that:
a) Finds if class implements interface
b) Finds if this particular class have a field that is implementing other interface
c) if above conditions are true checks if there is onStop() method overrided and if it is,
there should be another method call on field object.

Problem looks like
class implements classInterface{ // this must implement classInterface
   field
// this must implement fieldInterface
 
   onStop
(){
       field
.fieldMethodCall(); // field must call method
   
}
}


So i wrote detector that implements UastScanner
I am able to find class that implements classInterface but I have trouble with determining whenever field implements given interface. I have tried to write visitor and check it on visitClass method but seems like thats not how this tree looks like (Question 2 in the end of this post*). 
I think I could write last step logic myself -> onStop() + field that calls method inside


    @Override
   
public List<Class<? extends UElement>> getApplicableUastTypes() {
       
return Collections.singletonList(UClass.class);
   
}
 
   
@Override
   
public UElementHandler createUastHandler(JavaContext Jcontext) {
       
return new UElementHandler() {
           
@Override
           
public void visitClass(UClass uClass) {
               
JavaEvaluator evaluator = Jcontext.getEvaluator();


               
//get classes that implements interfaces
               
if (evaluator.implementsInterface(uClass, classInterface, true)) {
                   
//check all fields if they are instances of presenterinterface
                   
UField[] fields = uClass.getFields();
                   
if (fields.length > 0) {
                       
for (UField field : fields) {
                             
//this is where I am stucked
                             
//check if field implements interface
                       
}
                   
}
               
}
           
}
       
}
   
}


 And I have also few other questions;
 1) Are you going to create documentation of Uast / Psi apis in future? If yes, when?
 2) Is there a repository that cointais current android lint rules sources (migrated to new Uast api) available to developers?
 or any other open source projects you know, that contain some rules so I could read it?
 3) In one of slides your using method context.uastFile?.asRecursiveLogString() to see whole Log tree. I just can not find this method in java.



Tor Norbye

unread,
Jan 4, 2018, 10:53:39 AM1/4/18
to lint-dev


On Saturday, December 30, 2017 at 10:44:31 PM UTC+1, przem...@gmail.com wrote:
Hey 

Firstly I am quite noob in writing custom lint rules. I have read tutorials, and also watched some youtube videos (including KotlinConf 2017) about creating custom rules. 

I have been struggling with writing rule that:
a) Finds if class implements interface
b) Finds if this particular class have a field that is implementing other interface
c) if above conditions are true checks if there is onStop() method overrided and if it is,
there should be another method call on field object.


The easiest way to have your detector only apply for subclasses of a given class, or classes that implements an interface (or a subclass of an interface) is to use applicableSuperClasses(list) and visitClass; here's how ParcelDetector checks all classes that (possibly indirectly) implement Parcelable:

    override fun applicableSuperClasses(): List<String>? {
        return listOf("android.os.Parcelable")
    }

    override fun visitClass(context: JavaContext, declaration: UClass) {

To see if something implements/extends a type you would do something like this:

        for (field in declaration.fields) {
            val type = field.type
            if (type is PsiClassType) {
                val clz = type.resolve() ?: continue
                if (context.evaluator.inheritsFrom(clz, "your.pkg.Class", false)) {
                    // field is of type your.pkg.Class or some subclass/subinterface
                    // ...
                }
            }
        }
        

Now you can look at declaration.methods to see if onStop is present -- and if you need to do more fine grained check inside that method, run method.accept() and pass a visitor to it.

 And I have also few other questions;
 1) Are you going to create documentation of Uast / Psi apis in future? If yes, when?

Yes, probably when the APIs are stable (and sorry, I can't make any promises about future dates or deliveries.)

 2) Is there a repository that cointais current android lint rules sources (migrated to new Uast api) available to developers?

Yes, all the built-in checks were ported to UAST in 3.0 which is available as source; see
for checkout instructions. Once done, the lint source code is in tools/base/lint/
 
 or any other open source projects you know, that contain some rules so I could read it?

I think there are several; I saw some pointers here on this group/alias recently.
 
 3) In one of slides your using method context.uastFile?.asRecursiveLogString() to see whole Log tree. I just can not find this method in java.

That's one of the good reasons to use Kotlin; that's an extension method, so in the IDE it will let you find it (and many other utility methods) as if they're members on UElement.
To access it from Java use UastUtils.asRecursiveLogString and pass the element as the first argument.

-- Tor

Niklas Baudy

unread,
Jan 5, 2018, 7:06:41 AM1/5/18
to lint-dev
Reply all
Reply to author
Forward
0 new messages