Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Where-Used Search #429

Open
rvanbekkum opened this issue Dec 29, 2022 · 8 comments
Open

Where-Used Search #429

rvanbekkum opened this issue Dec 29, 2022 · 8 comments

Comments

@rvanbekkum
Copy link
Contributor

It might be nice to have a "Where-Used Search" feature that can tell us, 'where possible', where object/variable/procedure/symbol X is used across all project symbols.
So, taking it a step further than the "Find all references" command in VSCode when you are in .al files, instead searching for usages/references across all symbols in the workspace/project.
It might be nice if it could open an overview where you can see all references that were found (maybe similar to what you implemented for the duplicate code search and warning directives search).

I am not sure if Microsoft is working on something for this (in their AL Browser/GUI/... you name it :)), but I do not believe they are.
I do think it is quite a time investment, but I do think this might be very useful. It's been quite some times that I would really like to get some insights into where some object/symbol is used across all symbols, when I don't have the .al source files (N.B. not saying I do not have the source code, but for the "Base Application" I typically only have the .app symbol files and not a project with the .al source files :))

@fvet
Copy link
Contributor

fvet commented Jan 17, 2023

Adding @DavidFeldhoff since AL Code Actions already has some basic 'where-used' for table triggers.

https://github.com/DavidFeldhoff/al-codeactions#references-to-built-in-functions

@DavidFeldhoff
Copy link
Contributor

DavidFeldhoff commented Jan 17, 2023

Yes, exactly.. @anzwdev maybe we can have a chat about it? I added locally already something else and would like to talk with you about it

@anzwdev
Copy link
Owner

anzwdev commented Jan 19, 2023

Yes, we can have a chat. I have a plan how to write this functionality, it probably won't be very complex, but it will require recompilation of the base app and other symbols, so I am afraid that it will take a few seconds to get the results and memory consumption might be a bit high.

@bjarkihall
Copy link

Do you know what MS use for the Find All References feature? (or just to display the references number in the codelens)

I've been thinking about some rules/actions I'd like to play around with,
I noticed unused global/local variables can be removed with actions you created,
but I'd like to add a rule which removes all local/internal procedures that have 0 references.
local procedure is more easy, but internal procedures requires searching the whole project (and even including internalsVisibleTo project, like the Autotests, but I consider that more of a less-common edge-case).

I've created a rough script which removes all Obsoleted symbols up to a given version.
I run it whenever we bump to a new major release. It has an option to leave tables/table-fields/tableext-fields/enums/enum-values, since removing these can lead to a data-schema change, which makes upgrades more difficult.
But all it does is compile a syntax tree for each file, loop traverse its descendants and make sure they have Obsolete attribute with values lower than specified.
I have no idea how to cover more than 1 file in a tree and I've never found anything mentioning "references"/"uses".

Is there a function in the DLLs that can just give me all SyntaxNode's that reference the current SyntaxNode I have?
If MS doesn't provide it, have you reverse engineered it or made available somewhere in this repo to try out? :)

@rvanbekkum
Copy link
Contributor Author

Do you know what MS use for the Find All References feature? (or just to display the references number in the codelens)

I've been thinking about some rules/actions I'd like to play around with, I noticed unused global/local variables can be removed with actions you created, but I'd like to add a rule which removes all local/internal procedures that have 0 references. local procedure is more easy, but internal procedures requires searching the whole project (and even including internalsVisibleTo project, like the Autotests, but I consider that more of a less-common edge-case).

I've created a rough script which removes all Obsoleted symbols up to a given version. I run it whenever we bump to a new major release. It has an option to leave tables/table-fields/tableext-fields/enums/enum-values, since removing these can lead to a data-schema change, which makes upgrades more difficult. But all it does is compile a syntax tree for each file, loop traverse its descendants and make sure they have Obsolete attribute with values lower than specified. I have no idea how to cover more than 1 file in a tree and I've never found anything mentioning "references"/"uses".

Is there a function in the DLLs that can just give me all SyntaxNode's that reference the current SyntaxNode I have? If MS doesn't provide it, have you reverse engineered it or made available somewhere in this repo to try out? :)

We're using a (custom) code analyzer rule (implemented in C#) which detects which internal procedures and which public/internal procedures from internal objects are unused/not referenced. Maybe that could help for also making a code action out of it? That's what you would like to do if I understand correctly?

@bjarkihall
Copy link

Yes, I was looking at the Microsoft.Dynamics.Nav.CodeAnalysis DLL's namespace,

I've been using SyntaxTree.ParseObjectText (as described here) but I was only feeding it text content from a single file at a time and removing syntax nodes and saving the modified text back to the file, so I haven't really worked with a whole "project" (multiple files in a path).

The benefit was I only needed the MS DLLs, so I could run it standalone and even parallelized, without vscode.
I guess I could try looking into al-code-outline source code to see how it does things or just create a custom vscode extension to interact with it or directly with the AL Language Extension, if it exposes anything.
Another benefit was also that I wanted to learn how the parser/services work roughly under the hood.

I'm just unsure where/how I can have a service which contains the full syntax tree of a workspace and ask it for references (my case only needs to know if it has references in the first place). Similar to things like SyntaxNode.DescendantNodes / SyntaxNode.FirstAncestorOrSelf / SyntaxNode.IsKind(Kind.MemberAttribute) / ObjectCompilationUnitSyntax.RemoveNodes,

Is Microsoft.Dynamics.Nav.CodeAnalysis.Workspaces.FindReferences.FindReferencesService.FindReferencedSymbolsAsync maybe the key? I haven't used the Workspaces namespace yet.
The only blog I had to learn from is the one I linked to and from that I have only been reverse-engineering with trial-and-error. Is there a good material out there to get more familiar with how to work with the API?

I also wanted to make sure there wasn't someone else out there doing the same work at the same time, so if there's a rule coming to LinterCop / AZ then I could just wait (or help, if there's a PR for review or if it needs testing on a large solution).

In the end I'd like to use the Codecop rules for unused symbols in vscode and pipelines and it also needs the code-actions especially when projects want to be able to turn the rule on for the first time.
Thanks for the feedback btw.

@anzwdev
Copy link
Owner

anzwdev commented Sep 22, 2023

Hi

There is no documentation for AL compiler, but it is based on Roslyn, so you can read that documentation if you want to understand how it works. What you need to solve your problem is SemanticModel. This model is created by compiling all solution syntax trees and allows to get information about data types of your syntax tree nodes.

Code analyzers also have access to the SemanticModel for more complex rules that require knowledge about data types of processed nodes.

To create SemanticModel of your solution, you have to use Compilation class to compile all your syntax trees and then ask it for the model.

Here is the c# Roslyn documentation mentioning SemanticModel
https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/work-with-semantics

@bjarkihall
Copy link

Thanks, I think I kind of get the idea, this repo is a great resource as well:

protected void RemoveReferencedVariables(Dictionary<string, ISymbol> deleteVariables, SyntaxNode node)

I have once interacted with AZ Dev Tools via custom vscode extension while attending a workshop, but I had such a short time to dig any deeper, and it was on TS-level instead of .NET.
I might have to clone the repo and either try to add new actions to try it out, or use the DLLs directly. I also took a look at the LinterCop repo, which does its own parsing as well it seems, but it didn't seem to cover nearly as broad cases.

For the case of looking for unused local/internal procedures and the action of removing them, I'd prefer contributing directly to al-code-outline/LinterCop or at least help testing it if either or both parts are already implemented.
If the LinterCop rule is ready, the remove-action should be simple, since we have similar actions and utilities for unused variables.
Please reach out if you need help, I'll do my best.

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

No branches or pull requests

5 participants