Skip to content
This repository has been archived by the owner on Nov 3, 2024. It is now read-only.

Implementation of LombokReferenceSearcher and LombokMethodReferenceSearcher (fix for #685) #722

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

npiguet
Copy link

@npiguet npiguet commented Dec 31, 2019

This is still a WIP at this point, but is intended to fix the following issue:

"Analyze Data Flow" does not work with the lombok plugin #685

@npiguet npiguet changed the title Implementation of LombokReferenceSearcher and LombokMethodReferenceSearcher Implementation of LombokReferenceSearcher and LombokMethodReferenceSearcher (fix for #685) Dec 31, 2019
@npiguet
Copy link
Author

npiguet commented Dec 31, 2019

There's some encouraging progress. The plugin can now find field usages inside the body of generated methods, follow the references to the generated methods parameters, and then find usages of the generated method itself.

image

Note that in the current state, LombokFieldFindUsagesHandlerFactory is disabled in plugin.xml but that's is only meant as a debugging help, to avoid messing with the results I get from the other search components.

@mplushnikov
Copy link
Owner

Thank you Nicolas! It would be really great improvement! I already tried to get ReferenceSearcher to work, but has no success and have implemented FindUsageHandler instead.

…uilder.build() method. We can find the constructor call, but for some reason the parameters are bound to ParentClass.field instead of ParentClassBuilder.field, which messes up all the following resolution.

Not sure how to fix that.
…ternal implementation detail anyway. It also confuses the IDE, and causes it the replace the builder field with the original class field in the various Java Usage views.

2. Qualify builder fields accesses in getters using MyClassBuilder.this.fieldName. When using simply this.fieldName, the IDE gets confused and thinks we are referencing a field from the containing class. (pretty weird, not sure why it does that)
…Dataflow to Here" appears to work more or less correctly, but nodes in the usage tree have weird names.
…enerated PsiElements end up pointing to the original Java code file instead of null. This fixes ugly node views in the Find usages and Dataflow results.
@npiguet
Copy link
Author

npiguet commented Jan 3, 2020

It's getting better I think all the references are being found, but some nodes still don't look very descriptive when displayed in the "find usages" view, which is partially shared between "find usages" and "Dataflow to/from here".

@mplushnikov
Copy link
Owner

You are doing a really hard work! Really great!

I tried to figure out, why I have implemented LombokLightMethodBuilder#rebuildMethodFromString() method, this is very old code and maybe not nessairy any more, or used by some actions in Intellij, where I doesn't have automatically tests for it.

Maybe you can try to remove it and simplify your ReferenceSearch to work only with LightElements?

I only have find out, that LombokLightMethodBuilder#copy() should be implemented to support Intellij inline refactoring action (Ctrl + Alt + N) for lombok getters for example.
This can be replaced with:

  public PsiElement copy() {
    return new LightMethod(getManager(), this, getContainingClass());
  }

But this results in incorrect code, there 'this.' is not correctly applyed.
I think this could be generall problem of current implementation for CodeBlock generation. They are generated during AugmentProcessing and saved in LightMethod-Instances. They could still references some PsiElements which are changed or get invalidated some time later by IntelliJ...I think you saw something similar too?

@mplushnikov
Copy link
Owner

We can also try to ask Anna Kozlova (@akozlova) to help to get it done well. She already mention in https://youtrack.jetbrains.com/issue/IDEA-179790, that implementation of ReferenceSearch would be a good idea.

@npiguet
Copy link
Author

npiguet commented Jan 5, 2020

The thing about LightElements, is that they don't seem designed to handle more than declarations. So classes, methods, fields, etc... can be done, but statements, expressions, code blocks, etc... can't be done.

Using LightElement is good enough to implement "find usages" or "search for references" that just replaces searching for the field by searching for the methods. But "Dataflow to/from here" (SliceUtil, SliceForwardUtil) doesn't understand the references returned when we do that. When SliceUtil looks for variable references, it does a bunch of checks to see if it's in an assignment, if its on the RHS or LHS of the = operator, etc... This stuff does not translate well when we replace a field reference by a method reference.

So in the end, I think we should keep both the FindUsages handler and the ReferenceSearch/MethodReferenceSearch. That should cover both use cases.

…nd usages view when DummyHolder.originalFile is set is kind of more confusing than what was there before
@npiguet
Copy link
Author

npiguet commented Jan 5, 2020

It looks like in term of the display of the results, I can't do much better. each line of the results view is split in two:

  1. A syntax highlighted view of the line at which the reference was found, with the reference itself highlighted in bold.
  2. A light grey "in (...)" that tells you in which method or class that line exists.

It turns out that both of these elements are generated by IntelliJ, and we can't really tweak our implementation to get it to display right.

  1. The line is taken from the Document instance that is associated with the PsiFile containing the PsiElement. In our case, that file is a DummyHolder which is flagged as non-physical, non-system-event-enabled. That fact means that IntelliJ doesn't even try to find a matching line anywhere, and at best, just prints PsiElement.getText().
  2. I couldn't find the exact code for that "in (...)" bit, but it seems to just navigate the ancestors of the PsiElement until it finds either a PsiMethod or a PsiClass. Since the references are in method bodies, they are all children of the PsiMethodImpl version of the method, which doesn't have a proper parent. As a result, we only get "in method()" instead of "in ParentClass.method()".

In any case, the references themselves are returned correctly, but the way they are displayed is not great, and in the current state of things, it doesn't look like we can improve it further.

I still need to make the ReferenceSearcher and MethodReferenceSearcher a bit more thorough so they can find references properly for @Singular fields and @With classes.

LordOfThePigs added 3 commits January 5, 2020 23:22
…oduces junk node descriptions in the find usages tree. Returning null causes NPE. Return a dummy file instead.
…tructor references in @with methods, and should in general be more reliable.
@npiguet
Copy link
Author

npiguet commented Jan 5, 2020

Ok, at this point it looks like everything is working as expected. I get the same results when I run "Dataflow to here" on a class with lombok annotations as I get when I run it on the same class with all annotations de-lomboked.

The display of the results is a bit different due to the point mentioned earlier, but they are references to the same expressions.

On top of this small differences, there are a few caveats to which I couldn't find viable solutions:

  1. Find usages now shows some extra entries (up to 2) that are labelled just with "expression". That's the result of the IDE looking for reference to the field (which uses LombokReferenceSearcher) and getting results that point to generated code. Accessor and wither method references are found as usual.
  2. Setter methods on Builder classes are now delomboked to "MyClassBuilder.this.field = field" instead of "this.field = field". It's semantically the same when delomboked, but the prefix is required for IntelliJ to properly resolve field references in Builder methods.
  3. There are some weird areas that are highlighted at the top of the file whenever the caret is placed on top of a class field. This seems to be because the IDE tries to highlight the field usages returned by LombokReferenceSearcher, but since they are in generated code, it gets confused and generates a garbage highlight.

Please let me know if any of these side-effects are show stoppers.

Maybe we should should indeed ask for help from @akozlova to resolve those issues, do you know what is the best way to contact her?

@npiguet
Copy link
Author

npiguet commented Jan 6, 2020

Here is the sample project that I have used to test all this stuff.
sample.zip


public LombokMethodReferenceSearcher(boolean requireReadAction) {
super(requireReadAction);
System.out.println("LombokMethodReferenceSearcher(boolean)");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Care to remove System.out statements?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants