Using IntelliJ IDEA, How to inspect (lint) if the instance is not calling the method from its own class but from the super class?
Asked Answered
W

2

7

I want to check (actually prohibit) the call of the method of the super class from the instance of its subclass.

Example (here the Generics wouldn't be needed but I'm using it because I want to make this example close to my real situation):

abstract class GenericFoo<T> { }

class IntegerFoo extends GenericFoo<Integer> { }

class StringFoo extends GenericFoo<String> { }

abstract class GenericFooPrinter<T> {
  void print(GenericFoo<?> genericFoo) {
    System.out.println("GenericFooPrinter...");
    System.out.println(genericFoo);
  }
}

public class StringFooPrinter extends GenericFooPrinter<String> {
  void print(StringFoo stringFoo) {
    System.out.println("StringFooPrinter...");
    super.print(stringFoo);
  }
}

then

public class FooApplication {
  public static void main(String[] args) {
    var stringFooPrinter = new StringFooPrinter();
    var integerFoo = new IntegerFoo();
    var stringFoo = new StringFoo();

    // the types unmatch (`String` of the instance vs `Integer` of the argument),
    // so this calls `GenericFooPrinter.print`, which I want IntelliJ to check by custom-inspection, etc.
    stringFooPrinter.print(integerFoo);
    // this calls `StringFooPrinter.print`, which is okay
    // (though it actually calls `GenericFooPrinter.print` via `super` inside it).
    stringFooPrinter.print(stringFoo);
  }
}

Now, how should I make IntelliJ IDEA to inspect when stringFooPrinter is calling the method of its superclass, i.e. GenericFooPrinter.print ? Custom Inspection might be the way to go but any method that works is fine, but I don't want to change the method name to something else from .print.

(Real Situation - probably need not to understand: I'm trying to override jooq's .get and .set method in a generated subclass from its ancestor class AbstractRecord, and match the type of the argument TableField<XXXRecord> to the type parameter XXXRecord of its class, and inspect if the types unmatch and thus it calls the .get or .set of AbstractRecord against the wrong field. Chainging the method name would work but I want to avoid it. I know .getId(), etc. exists but I want to customize in this way.)

Waltman answered 2/6 at 20:51 Comment(3)
The normal solution would be to make the method abstract, so that the compiler can enforce that implementing classes overwrite it. Making a method available in a base class but not allowing people to use it seems weird. Why does it even exist in that case? I don't understand the last paragraph about jooq, but it sounds a bit like you're working against that framework.Roberts
@Roberts The method in a superclass is actually called from the subclasses as in my example. The jooq's .get is very permissive and actually accepts the wrong fields, too. It is usually called internally from a subclass with a proper field, but it is public and also allowed for the user to call by yourself (with wrong fields, too). For example, articleRecord.get(TITLE), which is actually calling AbstractRecord.get(TITLE), is correctly okay but unfortunatelly articleRecord.get(DATE_OF_BIRTH) is also "okey" as per argument type but causes runtime error.Waltman
what have u tried so far? did u check linter annotations?Darreldarrell
V
0

I know this is not a complete answer for your question but maybe you could use this approach to inspect the right methods.

You can use a Structural search inspection like this

$Instance$.$MethodCall$($Parameter$)

for your example if i declere a type modifier for $Instance$ as StringFooPrinter and for $Parameter$ as !StringFoo, that gives me all usages of function stringFooPrinter.print which is not type of StringFoo (I used text modifier for $MethodCall$ "print" to find a specific method)

Maybe you can use same approach for your main case.

enter image description here

enter image description here

enter image description here

Vasquez answered 10/6 at 18:18 Comment(1)
Interesting, but (as you notice) unfortunately, there're indefinite number of classes in reality - IntegerFoo, StringFoo, SpecialFoo, UltraFoo, ... and that's why I used Generics in my OP. Thus, this method does not work - I mean, I should really find a way to match the instance type and argument type in general.Waltman
R
0

This seems like the more general question of 'how do I get type parameters checked on things that are not properly typed', such as Map.get(), which takes an Object even though Map itself is typed. There are static checking tools to do this, and you can configure them in Idea. For example, 'ErrorProne' (https://errorprone.info/). ErrorProne is actually a compiler, so you might want to run it separately from your regular build (it advertises good checking, but I don't know about trusting performance or other details you might care about in production). You could probably configure FindBugs to tell you about the problem, but that'd require you to code up the checker. If you have a big project, it might be worth it.

Retrieve answered 10/6 at 20:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.