flutter-intellij icon indicating copy to clipboard operation
flutter-intellij copied to clipboard

Find Usages doesn't work well

Open marcglasberg opened this issue 3 years ago • 11 comments

If you press CTRL-ALT-F7 in a keyword that is used a lot, like a toString() declaration (in a large codebase), it will take FOREVER to finish. Actually I am not sure it would finish, I could never really wait enough to see it finish. I just see this:

Searching

Why does it take so long? It should be fast, since it should simply find the usages of toString() on objects of that specific class and its subclasses, which are very few. Or, it's trying to find usages of the upmost base class? That seems wrong. If I wanted to find usages for the base class I could just go to the base class and find usages there.

HOW TO REPRODUCE

Open any large codebase, go to any toString() declaration, and press CTRL-B (in Windows).

marcglasberg avatar Oct 07 '22 18:10 marcglasberg

Oops, sorry @marcglasberg I see a couple issues slipped by.

I'm not an expert on the analyzer, but I think I can explain why it checks everything. Anything defined in Object can be invoked on any object, in any piece of code. Finding the usages of toString() within the sub-hierarchy would be inaccurate, since it can be called from outside the hierarchy. You may know it is not going to be called from anywhere else, but the analyzer doesn't. You might try filing a request for a new feature, "Find usages in this hierarchy", in dart-lang/sdk.

@bwilkerson Anything to add?

stevemessick avatar Oct 17 '22 20:10 stevemessick

Sorry for the delay. I didn't notice this in my inbox earlier.

No, I don't really have anything to add. I think that covered it quite well.

What the analyzer is returning for references to a method is all of the places from which that method could be invoked. An instance of the class C can be assigned to any variable whose static type is a supertype of C, so in order to find all of the places where a method m on C could be invoked we have to look at the supertype chain to find the most general type G for which the method is defined, and then look for all of the invocations of m on any subtype of G.

The methods on Object are particularly bad in that respect because almost every type is a subtype of Object.

If I wanted to find usages for the base class I could just go to the base class and find usages there.

I don't know how common a use case that is, but it's definitely an interesting idea.

I don't have any evidence to support it, but personally I suspect that it's not at all uncommon to have a base class and multiple implementations of the base class, and that it would be confusing to many users to select a method in a subclass and ask for references, only to discover that the returned references didn't include all of the places where the selected method is invoked, but only those where the invocation site ensures that it has to either be that method or an overriding method in a subclass.

bwilkerson avatar Nov 11 '22 23:11 bwilkerson

I'm getting this issue on everything nowadays, including private widgets (StatelessWidgets whose names start with an underscore) where there is only one usage. Event after leaving the search dialog open for 10 minutes it does not find the usages, but after closing the dialog and opening it again the only usage is found instantly. Adding the widget in a second location of the widget tree seems to wipe whatever index/cache has been built up and I need to repeat the process. The green icons/boxes/marks on the right side of my code that mark the references to the code are updated instantly though and work just fine. My current workflow for finding references has been reduced to the following:

  • Go to the widget constructor,
  • Ctrl+click to start the search
  • If the search results don't pop up within two seconds I close the search dialog and repeat two times, and if it is still not finding the references I do a global text search for the constructors name.

This issue is becoming a big drag on my productivity.

LukasGlader avatar Jun 01 '24 10:06 LukasGlader

Hi. Any ideas how to resolve this issue? Probably this is also somehow related to monorepos

vasilich6107 avatar Jun 12 '24 15:06 vasilich6107

@jwren

bwilkerson avatar Jun 12 '24 16:06 bwilkerson

Same for me. Can't work with this being stuck. Considering moving to VSCode.

RaphaelJenni avatar Jul 08 '24 09:07 RaphaelJenni

@stevemessick

Anything defined in Object can be invoked on any object, in any piece of code. Finding the usages of toString() within the sub-hierarchy would be inaccurate, since it can be called from outside the hierarchy.

I'd say it doesn't matter where it's defined. It matters the class of the method the caret was when I asked for Find Usages. It I was in MyClass.toString() then it finds toString() usages for MyClass and its subclasses. It should not find in superclasses. For example, it should not find this:

void logError(Object? obj) => obj.toString();

But it would find those:

void logError(MyClass? obj) => obj.toString();
void logError(MyClassSubclass? obj) => obj.toString();

If I really want to find all potential usages, I can go to the base class that defined the method and Find Usages there.

In any case, the current Find Usages is unusable, as it takes forever to finish. What good is having a feature that perform so poorly we can't use it in practice. Anyway, I'll open another issue, as per your request.

marcglasberg avatar Jul 08 '24 16:07 marcglasberg

@stevemessick @bwilkerson

Here: https://github.com/dart-lang/sdk/issues/56176

But please don't close this issue. The real problem here is that Find Usages is too slow to be useful for large codebases.

marcglasberg avatar Jul 08 '24 17:07 marcglasberg

I'd say it doesn't matter where it's defined. It matters the class of the method the caret was when I asked for Find Usages. It I was in MyClass.toString() then it finds toString() usages for MyClass and its subclasses. It should not find in superclasses.

I'd agree with you if we were talking about the runtime type of the receiver, but unfortunately the Find Usages feature doesn't have that information. The only information we have is the "static type" of the receiver, which will always be (the same as or) a supertype of the runtime type.

Maybe your use case is different, but I primarily use Find Usages to figure out whether a change that I want to make to a method is likely to break existing code. Unfortunately, to answer that question, I need to find all of the places that might be impacted by the change, which is the same as all of the places where the method might be invoked at runtime. Limiting the search to only those places where the static type is the same as or a subtype of the class in which the method is defined would cause me to miss too many potential bugs, and I might not be alone in this.

The real problem here is that Find Usages is too slow to be useful for large codebases.

Right. And I suspect that the problem isn't limited to toString or other methods that are declared on Object.

bwilkerson avatar Jul 08 '24 18:07 bwilkerson

Hi @marcglasberg, it's been a while. I don't want you to think I'm ignoring you; I'm no longer on the Dart and Flutter teams.

stevemessick avatar Jul 08 '24 20:07 stevemessick

@stevemessick sure, Steve. Thanks for letting me know.

marcglasberg avatar Jul 09 '24 01:07 marcglasberg