Skip to content

PoC_add_new_pragma_lint #22717

Draft
gorsing wants to merge 2 commits intodlang:masterfrom
gorsing:refactoring_add_pragma_lint
Draft

PoC_add_new_pragma_lint #22717
gorsing wants to merge 2 commits intodlang:masterfrom
gorsing:refactoring_add_pragma_lint

Conversation

@gorsing
Copy link
Contributor

@gorsing gorsing commented Mar 10, 2026

Motivation

Currently, developers rely on external tools like D-Scanner to catch semantic anti-patterns and style issues. However, external AST-based linters lack access to the compiler's symbol table and type resolution, which leads to false positives/negatives for complex semantic rules.
At the same time, introducing new semantic checks as compiler warnings (-w) has historically been rejected because it breaks existing codebases and CI pipelines.

Solution

This PR introduces a dedicated, strictly opt-in linter infrastructure integrated directly into the frontend via pragma(lint).

  • Zero Breakage: Lint messages use a new ErrorKind.lint diagnostic that strictly does not increment global.errors or global.warnings. Compiling with -w or -we will never fail if a lint rule is triggered.
  • Lexical Scoping: pragma(lint, ruleName) integrates tightly with Scope and applies lexically. It can be toggled on/off (none, all) per-module, per-aggregate, or per-function, respecting D's philosophy of explicit control.
  • Tooling Integration: Lint diagnostics are fully compatible with the existing SARIF output, making it ready for modern IDEs and CI/CD environments.

Proof of Concept Rule (constSpecial)

To demonstrate the infrastructure, this PR includes one zero-false-positive rule: constSpecial. It emits a lint message when special struct methods (opEquals, toHash, opCmp, toString) are declared without const. Since this check sits in semantic3.d (visit(FuncDeclaration)), it leverages the compiler's perfect knowledge of types and correctly ignores compiler-generated thunks (!funcdecl.isGenerated()).

Example usage:

pragma(lint, constSpecial):

struct S {
    // Triggers: "Lint: special method `opEquals` should be marked as `const`"
    bool opEquals(ref const S other) { return true; } 
}

pragma(lint, none): // Disable for subsequent declarations

Future Impact

If accepted, this lays the groundwork for migrating highly requested, semantically heavy checks (e.g., unused parameters, redundant expressions, catching base Exception or UnusedParams) directly into the compiler, without disrupting the ecosystem or forcing warnings on users who don't want them.

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @gorsing! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + dmd#22717"

@gorsing gorsing marked this pull request as draft March 10, 2026 12:06
@gorsing
Copy link
Contributor Author

gorsing commented Mar 10, 2026

@rikkimax @tgehr Hello I try implement idea pragma(lint, lintParams);
This is first step implement idea from ##22695

@gorsing gorsing marked this pull request as ready for review March 10, 2026 12:19
@gorsing gorsing marked this pull request as draft March 10, 2026 12:39
@gorsing gorsing force-pushed the refactoring_add_pragma_lint branch from 472a33a to 46ef580 Compare March 10, 2026 13:19
@gorsing gorsing marked this pull request as ready for review March 10, 2026 16:49
@gorsing gorsing requested a review from ibuclaw March 10, 2026 16:50
@tgehr
Copy link
Contributor

tgehr commented Mar 14, 2026

  • Forcing transitive physical const on abstract data types is a bad idea, even for methods that should clearly be logically const. It would be better to just drop this and implement Manu's parameter usage check instead.
  • There should be a way to disable individual checks, now you can just enable them all, enable them one by one or turn off all at once. This is not useful in practice.
  • The lint error message should contain the identifier describing the diagnostic that would need to be disabled to get rid of the error.
  • "Does NOT increase error or warning counts." No, it has to be a hard error.

@gorsing
Copy link
Contributor Author

gorsing commented Mar 16, 2026

  • Forcing transitive physical const on abstract data types is a bad idea, even for methods that should clearly be logically const. It would be better to just drop this and implement Manu's parameter usage check instead.

    • There should be a way to disable individual checks, now you can just enable them all, enable them one by one or turn off all at once. This is not useful in practice.

    • The lint error message should contain the identifier describing the diagnostic that would need to be disabled to get rid of the error.

    • "Does NOT increase error or warning counts." No, it has to be a hard error.

@tgehr Thanks for the feedback! This is exactly the structural analysis I was hoping for. You've made excellent points.

Transitioning to unusedParams: I completely agree that Manu's parameter usage check is a more high-value target for a PoC. I’ve integrated the core logic from @TurkeyMan's work into this pragma(lint) infrastructure. I will keep constSpecial as a secondary, strictly optional rule to further demonstrate how the system handles different semantic phases.

Disabling individual checks: Agreed. For now, the implementation supports enabling specific rules or all of them. Granular "disable-only" toggling can be added as the list of rules grows.

Diagnostic identifiers: I'll update the lint() reporting mechanism to explicitly include the rule identifier. This ensures developers know exactly which rule triggered the message (e.g., Lint[unusedParams]: ...).

Hard Error: You’re right if a developer explicitly opts into a check via pragma, it should be treated with the same severity as any other language rule. I'll update ErrorKind.lint to increment global.errors. This aligns with D's philosophy of avoiding "ignored" warnings.

@gorsing gorsing changed the title refactoring_add_new_pragma_lint poc_add_new_pragma_lint Mar 16, 2026
@gorsing gorsing changed the title poc_add_new_pragma_lint PoC_add_new_pragma_lint Mar 16, 2026
@gorsing gorsing force-pushed the refactoring_add_pragma_lint branch from 8dab004 to ebc93d8 Compare March 17, 2026 10:44
@gorsing gorsing closed this Mar 17, 2026
@gorsing gorsing force-pushed the refactoring_add_pragma_lint branch from 6d7f5ec to e3c9e82 Compare March 17, 2026 11:52
@gorsing gorsing reopened this Mar 17, 2026
* Refactoring add pragma lint (#72)

* fix .h

* Refactoring st3 (#65)

* fix extern (C++)

* Auto-update C++ headers via dtoh

* make refactoring (#68)

* Refactoring add pragma lint v6 (#70)

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* Refactoring add pragma lint v6 (#71)

* Refactoring add pragma lint v5 (#69)

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* Refactoring add pragma lint v6 (#70) (#64)

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* add unusedParams

* update *.h

* refactoring
@gorsing gorsing force-pushed the refactoring_add_pragma_lint branch from eb2e904 to 890d23a Compare March 17, 2026 12:11
@gorsing
Copy link
Contributor Author

gorsing commented Mar 17, 2026

@rikkimax, since you proposed the pragma(lint) infrastructure, and @TurkeyMan, as you've done the heavy lifting on the unusedParams logic, I’d really value your input on this. I believe we can find a path that satisfies everyone’s requirements and helps D evolve.

I’ve put together a functional prototype in #22717 that merges both approaches: it takes the core logic from the unused parameters PR and wraps it in a lexically scoped pragma.

Having a concrete, testable implementation allows us to move past the philosophical debate and refine the actual mechanics. It’s designed to be strictly opt-in, posing no risk to existing codebases, while giving us a solid engine for higher quality standards—and perhaps eventually a way to safely modernize Phobos.

I'd specifically appreciate your technical thoughts on the scope handling in the implementation. I want to ensure this foundation is robust enough for D's future linting needs.

@gorsing gorsing requested a review from 0xEAB March 17, 2026 14:37
@rikkimax
Copy link
Contributor

I don't want this to be part of semantic analysis; it is complicated enough as it is, without introducing an additional optional feature.

It should be split out into its own package (dmd.lint), which runs after semantic analysis has completed. If you need multiple passes, then so be it.

Split out any state into their own extern(D) struct, which can be heap allocated by the lint pass as required.
This prevents them from going into the c++ header files; the fields in the AST nodes can be represented with void*.
See what I did for the fast DFA engine's state:

ParametersDFAInfo* parametersDFAInfo;

To move forward on this, it'll need to go to a monthly meeting to discuss. This month's meeting, I wasn't in a good state to deal with it. I was barely able to attend, and by the end, I had been up 20+ hours.

Good attempt at the idea, nonetheless.

@gorsing gorsing marked this pull request as draft March 18, 2026 10:05
@gorsing
Copy link
Contributor Author

gorsing commented Mar 18, 2026

@rikkimax Thanks for the feedback! This is exactly the analysis I was hoping for. You made grate point comments.
I'm will trying to create and use the lint.d module. May will be i create paralel branch for compare PoC v1 (semantic3.d)PoC v2(lint.d)

@TurkeyMan
Copy link
Contributor

TurkeyMan commented Mar 19, 2026

I'm quite unhappy that this has spun off from my issue in this way... I don't understand how this is any different than a warning, just presented in an unprecedented way.
Why would I prefer this to a normal warning which I can turn on and off just as easily? This is just another case of D doing "weird shit" that nobody expects for no reason. This is clearly a warning system, just make it a warning!

@TurkeyMan
Copy link
Contributor

pragma(warn, constSpecial): - There... I fixed it.
It seems we're dealing with an emotional reaction to the word "warning".
If you want pragma based warning control, then add pragma(warn).

@tgehr
Copy link
Contributor

tgehr commented Mar 20, 2026

I'm quite unhappy that this has spun off from my issue in this way... I don't understand how this is any different than a warning, just presented in an unprecedented way. Why would I prefer this to a normal warning which I can turn on and off just as easily?

This does not fork the language into multiple dialects.

This is just another case of D doing "weird shit" that nobody expects for no reason. This is clearly a warning system, just make it a warning!

Just put pragma(lint) and it will do what you wanted without burdening others.

@tgehr
Copy link
Contributor

tgehr commented Mar 20, 2026

pragma(warn, constSpecial): - There... I fixed it. It seems we're dealing with an emotional reaction to the word "warning". If you want pragma based warning control, then add pragma(warn).

I really don't care what it's called. The mechanism is what's important, and it's clearly you who is having an emotional reaction to terminology. x)

@TurkeyMan
Copy link
Contributor

TurkeyMan commented Mar 20, 2026

pragma(warn, constSpecial): - There... I fixed it. It seems we're dealing with an emotional reaction to the word "warning". If you want pragma based warning control, then add pragma(warn).

I really don't care what it's called. The mechanism is what's important, and it's clearly you who is having an emotional reaction to terminology. x)

I mean, I'm having an emotional reaction to arbitrarily renaming something that's established for at least half a century that everyone expects and understands for 'trendy'(?) reasons...

I still just want selective -w though, although pragma(warn) is a perfectly fine pursuit if you like it; I would use it in many situations.

@rikkimax
Copy link
Contributor

rikkimax commented Mar 20, 2026 via email

@TurkeyMan
Copy link
Contributor

I'm quite unhappy that this has spun off from my issue in this way... I don't understand how this is any different than a warning, just presented in an unprecedented way. Why would I prefer this to a normal warning which I can turn on and off just as easily?

This does not fork the language into multiple dialects.

This is just another case of D doing "weird shit" that nobody expects for no reason. This is clearly a warning system, just make it a warning!

Just put pragma(lint) and it will do what you wanted without burdening others.

Don't type -w and you won't be burdened! But selective warnings are a perfectly fine way to solve the odd opt-out/in case.

It's a serious stretch to say a warning fork's the language; C/C++ adds a bunch of new warnings every release, and compilers also have their own different sets. I've never heard anybody complain about this; if one is too aggressive or incompatible, disable it, or don't-enable it.
Virtually every other language also has warnings; of all the programming things that people broadly complain about, this is really not one of them.

We've spent more time talking about this here now than I reckon I've struggled with warning management in a lifetime.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants