Jump to page: 1 2
Thread overview
Unused import tool
Aug 15, 2023
RazvanN
Aug 15, 2023
Timon Gehr
Aug 15, 2023
RazvanN
Aug 18, 2023
Walter Bright
Aug 20, 2023
Walter Bright
Aug 21, 2023
FeepingCreature
Aug 22, 2023
Walter Bright
Aug 22, 2023
FeepingCreature
Aug 20, 2023
RazvanN
Aug 20, 2023
Walter Bright
Aug 15, 2023
cc
Aug 16, 2023
Basile B.
Aug 16, 2023
RazvanN
Aug 16, 2023
Basile B.
Aug 16, 2023
Basile B.
Aug 16, 2023
Basile B.
August 15, 2023

Hello everyone,

I have started working on a tool that identifies unused imports. It uses dmd as a library.

The purpose is to have a working tool, but also to better understand what sort of information the frontend needs to output so that such tools are possible.

Up to this point, I managed to make it work with global imports (since dmdfe currently does not provide the scope hierarchy). The tool does output some false positives because semantic is destructive and some things like aliases or enums are substituted eagerly and you cannot trace their origin.

I have tested this tool with some dmd sources and have already identified unused imports·

So, if you have projects with lots of global imports you could use it to at least narrow down the list of potential unused imports.

The tool is a work in progress, so if you have any ideas for improvement feel free to ping me.

Note that the import paths used are from my machine, so you will need to update those accordingly to the files imported in your project.

RazvanN

August 15, 2023
On 8/15/23 10:13, RazvanN wrote:
> 
> I have tested this tool with some dmd sources and have already identified [unused imports](https://github.com/dlang/dmd/pulls?q=is%3Apr+delete+unused+imports+author%3ARazvanN7+is%3Aopen
Not a big complaint, just something of which to be aware: Removing imports that are actually unused but can be relevant to the module will break forks on rebase. E.g., the removal of `import dmd.tokens` from `attrib.d` broke my implementation of UnpackDeclaration in the `tuple-syntax` branch.
August 15, 2023
On Tuesday, 15 August 2023 at 09:28:53 UTC, Timon Gehr wrote:
> On 8/15/23 10:13, RazvanN wrote:
>> 
>> I have tested this tool with some dmd sources and have already identified [unused imports](https://github.com/dlang/dmd/pulls?q=is%3Apr+delete+unused+imports+author%3ARazvanN7+is%3Aopen>
> Not a big complaint, just something of which to be aware: Removing imports that are actually unused but can be relevant to the module will break forks on rebase. E.g., the removal of `import dmd.tokens` from `attrib.d` broke my implementation of UnpackDeclaration in the `tuple-syntax` branch.

There are other situations where this is problematic: versioned statements, debug code etc. However, I would argue that useless imports for various builds of the compiler should be removed as they come with a compilation penalty. The solution to this is to make the imports more localized (e.g. put the import in the versioned block that is used - although this is not ideal if 2 multiple versions use the same import).

As for your specific case, I think it can be seen as removing a useless import that is then re-added once the patch is pushed to upstream. This is no different from any other interface modification: if someone adds a parameter to a function that you are using in your fork, then that will also break your code. Anyway, thanks for the heads up!

RazvanN
August 15, 2023

On Tuesday, 15 August 2023 at 08:13:04 UTC, RazvanN wrote:

>

I have started working on a tool that identifies unused imports. It uses dmd as a library.

Not sure how relevant this is to your methods, but something I used to stumble into occasionally:

import std.format;
import std.range.primitives;
struct S {
	void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
		writer.formattedWrite("ABC");
	}
}
void main() {
	S().writeln;
}

Output: ABC

import std.format;
//import std.range.primitives;
struct S {
	void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
		writer.formattedWrite("ABC");
	}
}
void main() {
	S().writeln;
}

Output: S()

The latter still compiles successfully and does not issue an error for the undefined template isOutputRange. This is an issue with std.format.internal's hasToString I believe, since, because there is no toString that would successfully pass __traits(compiles) (due to the undefined symbol), it never actually attempts to call the toString, therefore the template isn't compiled at all and thus no bark for the problem. But worth mentioning IMO as an example of a process that fails to identify undefined symbols when an import is missing.

August 16, 2023

On Tuesday, 15 August 2023 at 08:13:04 UTC, RazvanN wrote:

>

Hello everyone,

I have started working on a tool that identifies unused imports. It uses dmd as a library.

The purpose is to have a working tool, but also to better understand what sort of information the frontend needs to output so that such tools are possible.

Up to this point, I managed to make it work with global imports (since dmdfe currently does not provide the scope hierarchy).

You can handle nested import declarations without the scope. The trick would be to use a single visitor and a 2D import list, i.eImportInfo[][] imports;. The first dim matches to a scope, so when you enter a scope (BlockStmt, AggrDecl, etc) you push the local imports, and when you leave the scope, you check and pop. Styx Example, (which has same semantics as D about out-of-order decls).

>

The tool does output some false positives because semantic is destructive and some things like aliases or enums are substituted eagerly and you cannot trace their origin.

This is why I think that such a tool should be integrated to the compiler.

The only way (I know of today) to detect accurately whether an alias is used is to put of flag in the ast node matching to imports. For dmd that flag would be set in Import.search().

>

I have tested this tool with some dmd sources and have already identified unused imports·

So, if you have projects with lots of global imports you could use it to at least narrow down the list of potential unused imports.

The tool is a work in progress, so if you have any ideas for improvement feel free to ping me.

Note that the import paths used are from my machine, so you will need to update those accordingly to the files imported in your project.

Again another reason to integrate the tool to the compiler. There would be no need to write a driver, just a new option, let's say "--check-unused-import", and "basta".

Also I see no use case where the tool would be used with different import paths than those passed to the compiler.

>

RazvanN

August 16, 2023
On 16/08/2023 5:26 PM, Basile B. wrote:
> Again another reason to integrate the tool to the compiler. There would be no need to write a driver, just a new option, let's say "--check-unused-import", and "basta".

It would be part of dscanner that is built on dmd-fe.

Dscanner in recent versions is getting the ability to 'fix' code like dfix did.

So not only could dscanner check for unused imports, but it could remove them. Or even ideally add imports.
August 16, 2023

On Wednesday, 16 August 2023 at 05:26:11 UTC, Basile B. wrote:

>

You can handle nested import declarations without the scope. The trick would be to use a single visitor and a 2D import list, i.eImportInfo[][] imports;. The first dim matches to a scope, so when you enter a scope (BlockStmt, AggrDecl, etc) you push the local imports, and when you leave the scope, you check and pop. Styx Example, (which has same semantics as D about out-of-order decls).

Indeed, there are solutions to this problem. Another one is to treat scope as dscanner does now (the scope is just an integer which is incremented and decremented whenever a scope is entered/left), however, I was hoping to leverage most of the existing logic in the compiler.

>

This is why I think that such a tool should be integrated to the compiler.

I am now looking for a way to integrate this in the compiler to better understand what the compiler would need to expose in order to properly implement this. I thought it would be easy, unfortunately, when dmd does symbol resolution, the search methods work on a list of imported scopes (e.g. modules or mixins) and currently there is no way to tie the imported scope to a specific import declaration/statement. DMD's code could be altered to store the import instead of the module, however, Walter has made it clear that such linting behavior will not be accepted in dmd.

RazvanN

August 16, 2023

On Wednesday, 16 August 2023 at 07:27:34 UTC, RazvanN wrote:

>

On Wednesday, 16 August 2023 at 05:26:11 UTC, Basile B. wrote:

>

You can handle nested import declarations without the scope. [..]
Walter has made it clear that such linting behavior will not be accepted in dmd

I know, but he should make an exception for unused imports. That's really a check that is tied to which and how sources are compiled. I find absurd to have to write a driver that will handle the exact same arguments, while that would just work if done in the compiler, to be frank.

It's another story when it's about linting something localized. But in the case we're talking about, well, you've get my point.

August 16, 2023

On Wednesday, 16 August 2023 at 05:26:11 UTC, Basile B. wrote:

>

On Tuesday, 15 August 2023 at 08:13:04 UTC, RazvanN wrote:

>

Hello everyone,

I have started working on a tool that identifies unused imports. It uses dmd as a library.

The purpose is to have a working tool, but also to better understand what sort of information the frontend needs to output so that such tools are possible.

Up to this point, I managed to make it work with global imports (since dmdfe currently does not provide the scope hierarchy).

You can handle nested import declarations without the scope. The trick would be to use a single visitor and a 2D import list, i.eImportInfo[][] imports;. The first dim matches to a scope, so when you enter a scope (BlockStmt, AggrDecl, etc) you push the local imports, and when you leave the scope, you check and pop. Styx Example, (which has same semantics as D about out-of-order decls).

To be clear the styx version had the same problem as those you mention, until that commit. The flag mentioned previously, and that is set here (implementation of search is a bit different...), did not cause any slowdown.

To conclude, I dont see how such a tool could work correctly without being integrated directly in the compiler.

August 16, 2023

On Wednesday, 16 August 2023 at 17:17:22 UTC, Basile B. wrote:

>

On Wednesday, 16 August 2023 at 05:26:11 UTC, Basile B. wrote:

>

[...]

To be clear the styx version had the same problem as those you mention, until that commit. The flag mentioned previously, and that is set here (implementation of search is a bit different...), did not cause any slowdown.

To conclude, I dont see how such a tool could work correctly without being integrated directly in the compiler.

Otherwise another problem you may encounter is related to dot chains. only the LHS needs to be checked. The RHS might be resolved from the module where the LHS was found ;)

« First   ‹ Prev
1 2