From 6b14d19794a0e15c85dc6c258ab15fb8709c7834 Mon Sep 17 00:00:00 2001 From: Sergii K Date: Mon, 30 Mar 2026 17:04:38 +0300 Subject: [PATCH 1/9] Refactoring add pragma lint v11 (#81) * PoC_v2 --- compiler/src/build.d | 1 + compiler/src/dmd/frontend.h | 43 +++ compiler/src/dmd/func.d | 2 + compiler/src/dmd/id.d | 7 + compiler/src/dmd/lint/engine.d | 339 ++++++++++++++++++ compiler/src/dmd/main.d | 3 + compiler/src/dmd/pragmasem.d | 8 + compiler/test/compilable/lint_unused_params.d | 15 + .../test/compilable/lint_unused_params2.d | 19 + .../fail_compilation/lint_const_special.d | 76 ++++ .../fail_compilation/lint_unused_params.d | 72 ++++ compiler/test/runnable/template9.d | 1 + 12 files changed, 586 insertions(+) create mode 100644 compiler/src/dmd/lint/engine.d create mode 100644 compiler/test/compilable/lint_unused_params.d create mode 100644 compiler/test/compilable/lint_unused_params2.d create mode 100644 compiler/test/fail_compilation/lint_const_special.d create mode 100644 compiler/test/fail_compilation/lint_unused_params.d diff --git a/compiler/src/build.d b/compiler/src/build.d index 0dd0aa4b36fb..12701c2ceb4d 100755 --- a/compiler/src/build.d +++ b/compiler/src/build.d @@ -1592,6 +1592,7 @@ auto sourceFiles() visitor/strict.d visitor/transitive.d cparse.d dfa/entry.d dfa/utils.d dfa/fast/structure.d dfa/fast/analysis.d dfa/fast/report.d dfa/fast/expression.d dfa/fast/statement.d + lint/engine.d "), backendHeaders: fileArray(env["C"], " cc.d cdef.d cgcv.d code.d dt.d el.d global.d diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 4d74a27cbbcb..6a451f5ae753 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -3819,6 +3819,7 @@ class FuncDeclaration : public Declaration AttributeViolation* pureViolation; AttributeViolation* nothrowViolation; ParametersDFAInfo* parametersDFAInfo; + void* lintInfo; bool purityInprocess() const; bool purityInprocess(bool v); bool safetyInprocess() const; @@ -5991,6 +5992,14 @@ enum class CPU : uint8_t native = 12u, }; +enum class LintFlags : uint32_t +{ + none = 0u, + constSpecial = 1u, + unusedParams = 2u, + all = 4294967295u, +}; + enum class ErrorPrintMode : uint8_t { simpleError = 0u, @@ -7907,6 +7916,35 @@ class StrictVisitor : public ParseTimeVisitor virtual void visit(typename AST::CInitializer ) override; }; +class LintVisitor final : public Visitor +{ +public: + using Visitor::visit; + _d_dynamicArray< LintFlags > flagsStack; + void* unusedTrack; + LintVisitor(); + LintFlags currentFlags(); + void visit(Dsymbol* s) override; + void visit(Statement* s) override; + void visit(Expression* e) override; + void visit(Module* m) override; + void visit(AttribDeclaration* ad) override; + void visit(PragmaDeclaration* pd) override; + void visit(PragmaStatement* ps) override; + void visit(AggregateDeclaration* ad) override; + void visit(TemplateInstance* ti) override; + void visit(FuncDeclaration* fd) override; + void visit(VarExp* ve) override; + void visit(CompoundStatement* s) override; + void visit(ExpStatement* s) override; + void visit(IfStatement* s) override; + void visit(ReturnStatement* s) override; + void visit(ForStatement* s) override; + void visit(BinExp* e) override; + void visit(UnaExp* e) override; + void visit(CallExp* e) override; +}; + extern _d_real creall(complex_t x); extern _d_real cimagl(complex_t x); @@ -8916,6 +8954,11 @@ struct Id final static Identifier* ident; static Identifier* packed; static Identifier* op; + static Identifier* lint; + static Identifier* constSpecial; + static Identifier* unusedParams; + static Identifier* none; + static Identifier* all; static void initialize(); Id() { diff --git a/compiler/src/dmd/func.d b/compiler/src/dmd/func.d index a2ccc4c2256f..cef28894cded 100644 --- a/compiler/src/dmd/func.d +++ b/compiler/src/dmd/func.d @@ -302,6 +302,8 @@ extern (C++) class FuncDeclaration : Declaration ParametersDFAInfo* parametersDFAInfo; + void* lintInfo; + /// See the `FUNCFLAG` struct import dmd.common.bitfields; mixin(generateBitFields!(FUNCFLAG, uint)); diff --git a/compiler/src/dmd/id.d b/compiler/src/dmd/id.d index 228db546d91f..071780b548f3 100644 --- a/compiler/src/dmd/id.d +++ b/compiler/src/dmd/id.d @@ -548,6 +548,13 @@ immutable Msgtable[] msgtable = // for inline assembler { "op" }, + // lint + { "lint" }, + { "constSpecial" }, + { "unusedParams" }, + { "none" }, + { "all" }, + ]; diff --git a/compiler/src/dmd/lint/engine.d b/compiler/src/dmd/lint/engine.d new file mode 100644 index 000000000000..9a799992d006 --- /dev/null +++ b/compiler/src/dmd/lint/engine.d @@ -0,0 +1,339 @@ +module dmd.lint.engine; + +import dmd.aggregate; +import dmd.arraytypes; +import dmd.astenums; +import dmd.attrib; +import dmd.declaration; +import dmd.dmodule; +import dmd.dsymbol; +import dmd.dtemplate; +import dmd.expression; +import dmd.func; +import dmd.id; +import dmd.statement; +import dmd.visitor; +import dmd.init; + +import dmd.errors : warning; + +extern (D) enum LintFlags : uint +{ + none = 0, + constSpecial = 1 << 0, + unusedParams = 1 << 1, + all = ~0 +} + +private struct TrackedParam +{ + VarDeclaration decl; + bool used; +} + +extern(C++) final class LintVisitor : Visitor +{ + alias visit = Visitor.visit; + + LintFlags[] flagsStack; + TrackedParam[] activeParams; + + this() + { + flagsStack ~= LintFlags.none; + } + + LintFlags currentFlags() + { + return flagsStack.length > 0 ? flagsStack[$ - 1] : LintFlags.none; + } + + override void visit(Dsymbol s) { } + override void visit(Statement s) { } + override void visit(Expression e) { } + override void visit(Initializer i) { } + + override void visit(DeclarationExp de) + { + if (de && de.declaration) + de.declaration.accept(this); + } + + //override void visit(AliasDeclaration ad) + //{ + // if (ad && ad.aliassym) + // ad.aliassym.accept(this); + //} + + override void visit(Module m) + { + if (!m || !m.members) return; + foreach (s; *m.members) + if (s) s.accept(this); + } + + override void visit(AttribDeclaration ad) + { + if (ad && ad.decl) + { + foreach (s; *ad.decl) + if (s) s.accept(this); + } + } + + override void visit(PragmaDeclaration pd) + { + if (!pd) return; + bool pushed = pushLintFlags(pd); + + if (pd.decl) + { + foreach (s; *pd.decl) + if (s) s.accept(this); + } + + if (pushed) + flagsStack.length--; + } + + override void visit(PragmaStatement ps) + { + if (!ps) return; + bool pushed = false; + if (ps.ident == Id.lint) + { + pushed = true; + flagsStack ~= parsePragmaArgs(ps.args); + } + + if (ps._body) + ps._body.accept(this); + + if (pushed) + flagsStack.length--; + } + + override void visit(AggregateDeclaration ad) + { + if (!ad || !ad.members) return; + foreach (s; *ad.members) + if (s) s.accept(this); + } + + override void visit(TemplateInstance ti) + { + if (!ti || !ti.members) return; + foreach (s; *ti.members) + if (s) s.accept(this); + } + + override void visit(FuncDeclaration fd) + { + if (!fd) return; + + const flags = currentFlags(); + + if (flags & LintFlags.constSpecial) + checkConstSpecial(fd); + + bool checkUnused = (flags & LintFlags.unusedParams) != 0; + + if (checkUnused) + { + import dmd.astenums : LINK; + if (!fd.fbody || + (fd.vtblIndex != -1 && !fd.isFinalFunc()) || + (fd.foverrides.length > 0) || + (fd._linkage == LINK.c || fd._linkage == LINK.cpp || fd._linkage == LINK.windows)) + { + checkUnused = false; + } + } + + size_t paramStart = activeParams.length; + + if (checkUnused && fd.parameters) + { + for (size_t i = 0; i < fd.parameters.length; i++) + { + VarDeclaration v = (*fd.parameters)[i]; + + if (!v || !v.ident) continue; + + bool isIgnoredName = v.ident.toChars()[0] == '_'; + + if (!(v.storage_class & STC.temp) && !isIgnoredName) + { + activeParams ~= TrackedParam(v, false); + } + } + } + + if (fd.fbody) + fd.fbody.accept(this); + + if (checkUnused) + { + for (size_t i = paramStart; i < activeParams.length; i++) + { + if (!activeParams[i].used) + { + warning(activeParams[i].decl.loc, "[unusedParams] function parameter `%s` is never used", activeParams[i].decl.ident.toChars()); + } + } + } + + activeParams.length = paramStart; + } + + private void checkConstSpecial(FuncDeclaration fd) + { + if (fd.isGenerated() || (fd.storage_class & STC.const_) || (fd.type && fd.type.isConst())) + return; + + if (fd.ident != Id.opEquals && fd.ident != Id.opCmp && + fd.ident != Id.tohash && fd.ident != Id.tostring) + return; + + if (!fd.toParent2() || !fd.toParent2().isStructDeclaration()) + return; + + warning(fd.loc, "[constSpecial] special method `%s` should be marked as `const`", fd.ident ? fd.ident.toChars() : fd.toChars()); + } + + private bool pushLintFlags(PragmaDeclaration pd) + { + if (pd && pd.ident == Id.lint) + { + flagsStack ~= parsePragmaArgs(pd.args); + return true; + } + return false; + } + + private LintFlags parsePragmaArgs(Expressions* args) + { + LintFlags newFlags = currentFlags(); + if (!args || args.length == 0) + { + newFlags |= LintFlags.all; + } + else + { + foreach (arg; *args) + { + if (!arg) continue; + auto id = arg.isIdentifierExp(); + if (!id) continue; + + if (id.ident == Id.constSpecial) + newFlags |= LintFlags.constSpecial; + else if (id.ident == Id.unusedParams) + newFlags |= LintFlags.unusedParams; + else if (id.ident == Id.none) + newFlags = LintFlags.none; + else if (id.ident == Id.all) + newFlags |= LintFlags.all; + } + } + return newFlags; + } + + override void visit(VarExp ve) + { + if (!ve || !ve.var) return; + if (auto vd = ve.var.isVarDeclaration()) + { + for (size_t i = activeParams.length; i-- > 0; ) + { + if (activeParams[i].decl == vd) + { + activeParams[i].used = true; + break; + } + } + } + } + + override void visit(CompoundStatement s) + { + if (s && s.statements) + foreach (stmt; *s.statements) + if (stmt) stmt.accept(this); + } + + override void visit(ExpStatement s) + { + if (s && s.exp) s.exp.accept(this); + } + + override void visit(IfStatement s) + { + if (!s) return; + if (s.condition) s.condition.accept(this); + if (s.ifbody) s.ifbody.accept(this); + if (s.elsebody) s.elsebody.accept(this); + } + + override void visit(ReturnStatement s) + { + if (s && s.exp) s.exp.accept(this); + } + + override void visit(ForStatement s) + { + if (!s) return; + if (s._init) s._init.accept(this); + if (s.condition) s.condition.accept(this); + if (s.increment) s.increment.accept(this); + if (s._body) s._body.accept(this); + } + + override void visit(BinExp e) + { + if (!e) return; + if (e.e1) e.e1.accept(this); + if (e.e2) e.e2.accept(this); + } + + override void visit(UnaExp e) + { + if (e && e.e1) e.e1.accept(this); + } + + override void visit(CallExp e) + { + if (!e) return; + if (e.e1) e.e1.accept(this); + if (e.arguments) + foreach (arg; *e.arguments) + if (arg) arg.accept(this); + } + + override void visit(VarDeclaration vd) + { + if (vd && vd._init) + vd._init.accept(this); + } + + override void visit(ExpInitializer ei) + { + if (ei && ei.exp) + ei.exp.accept(this); + } + + override void visit(FuncExp fe) + { + if (fe && fe.fd) + fe.fd.accept(this); + } +} + +extern(D) void runLinter(Module[] modules) +{ + scope visitor = new LintVisitor(); + foreach (m; modules) + { + if (m) m.accept(visitor); + } +} diff --git a/compiler/src/dmd/main.d b/compiler/src/dmd/main.d index dbbde2889b6c..211696d0285d 100644 --- a/compiler/src/dmd/main.d +++ b/compiler/src/dmd/main.d @@ -72,6 +72,7 @@ import dmd.target; import dmd.timetrace; import dmd.utils; import dmd.vsoptions; +import dmd.lint.engine : runLinter; /** * DMD's entry point, C main. @@ -665,6 +666,8 @@ private int tryMain(const(char)[][] argv, out Param params) if (global.errors) removeHdrFilesAndFail(params, modules); + runLinter(modules[]); + // Scan for modules with always inline functions foreach (m; modules) { diff --git a/compiler/src/dmd/pragmasem.d b/compiler/src/dmd/pragmasem.d index d94414d9b5f3..effee44c15f8 100644 --- a/compiler/src/dmd/pragmasem.d +++ b/compiler/src/dmd/pragmasem.d @@ -206,6 +206,10 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc) .error(pd.loc, "%s `%s` takes no argument", pd.kind, pd.toPrettyChars); return declarations(); } + else if (pd.ident == Id.lint) + { + return declarations(); + } else if (!global.params.ignoreUnsupportedPragmas) { error(pd.loc, "unrecognized `pragma(%s)`", pd.ident.toChars()); @@ -329,6 +333,10 @@ bool pragmaStmtSemantic(PragmaStatement ps, Scope* sc) if (!pragmaMangleSemantic(ps.loc, sc, ps.args, decls.length ? &decls : null)) return false; } + else if (ps.ident == Id.lint) + { + // Linter will process this later. We accept it as a valid pragma. + } else if (!global.params.ignoreUnsupportedPragmas) { error(ps.loc, "unrecognized `pragma(%s)`", ps.ident.toChars()); diff --git a/compiler/test/compilable/lint_unused_params.d b/compiler/test/compilable/lint_unused_params.d new file mode 100644 index 000000000000..730e390e4617 --- /dev/null +++ b/compiler/test/compilable/lint_unused_params.d @@ -0,0 +1,15 @@ +/* +REQUIRED_ARGS: -w +TEST_OUTPUT: +---- +---- +*/ + +pragma(lint, unusedParams): + +class A +{ + void foo(int x) + { + } +} diff --git a/compiler/test/compilable/lint_unused_params2.d b/compiler/test/compilable/lint_unused_params2.d new file mode 100644 index 000000000000..5d582852b4c2 --- /dev/null +++ b/compiler/test/compilable/lint_unused_params2.d @@ -0,0 +1,19 @@ +/* +REQUIRED_ARGS: -w +TEST_OUTPUT: +---- +---- +*/ + +pragma(lint, unusedParams): + +class A +{ + void foo(int x) {} +} + +void main() +{ + auto a = new A(); + a.foo(1); +} diff --git a/compiler/test/fail_compilation/lint_const_special.d b/compiler/test/fail_compilation/lint_const_special.d new file mode 100644 index 000000000000..27760feae6e1 --- /dev/null +++ b/compiler/test/fail_compilation/lint_const_special.d @@ -0,0 +1,76 @@ +/* +REQUIRED_ARGS: -w +TEST_OUTPUT: +--- +fail_compilation/lint_const_special.d(20): Warning: [constSpecial] special method `opEquals` should be marked as `const` +fail_compilation/lint_const_special.d(25): Warning: [constSpecial] special method `toHash` should be marked as `const` +fail_compilation/lint_const_special.d(30): Warning: [constSpecial] special method `opCmp` should be marked as `const` +fail_compilation/lint_const_special.d(35): Warning: [constSpecial] special method `toString` should be marked as `const` +Error: warnings are treated as errors + Use -wi if you wish to treat warnings only as informational. +--- +*/ + +// Enable our new lint rule for the file +pragma(lint, constSpecial): + +struct BadStruct +{ + // LINT: special method `opEquals` should be marked as `const` + bool opEquals(ref const BadStruct rhs) { + return true; + } + + // LINT: special method `toHash` should be marked as `const` + hash_t toHash() { + return 0; + } + + // LINT: special method `opCmp` should be marked as `const` + int opCmp(ref const BadStruct rhs) { + return 0; + } + + // LINT: special method `toString` should be marked as `const` + string toString() { + return "BadStruct"; + } +} + +struct GoodStruct +{ + // OK: method is marked as const + bool opEquals(ref const GoodStruct rhs) const { + return true; + } + + // OK: method is marked as const + hash_t toHash() const { + return 1; + } + + // OK: method is marked as const + int opCmp(ref const GoodStruct rhs) const { + return 0; + } + + // OK: method is marked as const + string toString() const { + return "GoodStruct"; + } +} + +// Disable the linter for the following code +pragma(lint, none): + +struct IgnoredStruct +{ + // No lint messages here, as the linter is disabled! + bool opEquals(ref const IgnoredStruct rhs) { + return true; + } +} + +void main() +{ +} diff --git a/compiler/test/fail_compilation/lint_unused_params.d b/compiler/test/fail_compilation/lint_unused_params.d new file mode 100644 index 000000000000..583fd6dd3152 --- /dev/null +++ b/compiler/test/fail_compilation/lint_unused_params.d @@ -0,0 +1,72 @@ +/* +REQUIRED_ARGS: -w +TEST_OUTPUT: +---- +fail_compilation/lint_unused_params.d(17): Warning: [unusedParams] function parameter `y` is never used +fail_compilation/lint_unused_params.d(36): Warning: [unusedParams] function parameter `b` is never used +fail_compilation/lint_unused_params.d(52): Warning: [unusedParams] function parameter `b` is never used +fail_compilation/lint_unused_params.d(64): Warning: [unusedParams] function parameter `z` is never used +Error: warnings are treated as errors + Use -wi if you wish to treat warnings only as informational. +---- +*/ + +pragma(lint, unusedParams): + +// 1. Regular functions +void testBasic(int x, int y) +{ + cast(void)x; +} + +// 2. Interfaces and abstract methods (no body - ignored) +interface I { void ifaceMethod(int a); } +abstract class AbstractBase { abstract void absMethod(int a); } + +// 3. Virtual and overridden methods (ignored) +class Base { void foo(int a) {} } +class Derived : Base +{ + override void foo(int a) {} +} + +// 4. Final methods (cannot be overridden, so parameters must be used) +class Normal +{ + final void bar(int a, int b) + { + cast(void)a; + } +} + +// 5. Template functions (checked upon instantiation) +void tplFunc(T)(T x, T y) +{ + cast(void)x; +} +alias instantiateTpl = tplFunc!int; + +// 6. Delegates and lambdas +void testDelegate() +{ + auto dg = (int a, int b) { + cast(void)a; + }; +} + +// 7. Unnamed parameters (ignored by the compiler as they have STC.temp) +void unnamedParam(int, int x) +{ + cast(void)x; +} + +// 8. Default parameters +void defaultArg(int x = 5, int z = 10) +{ + cast(void)x; +} + +// 9. Disable linter for the rest of the file +pragma(lint, none): + +void completelyIgnored(int a) {} diff --git a/compiler/test/runnable/template9.d b/compiler/test/runnable/template9.d index 42d64495604d..b9c4598b9bf8 100644 --- a/compiler/test/runnable/template9.d +++ b/compiler/test/runnable/template9.d @@ -52,6 +52,7 @@ Success module breaker; +pragma(lint, none): import core.stdc.stdio, core.vararg; /**********************************/ From 5b8c8a9073d52ca5b5166bc26af7fb572affad3a Mon Sep 17 00:00:00 2001 From: Sergii Kuzko Date: Mon, 30 Mar 2026 17:58:00 +0300 Subject: [PATCH 2/9] PoC_v2.1 --- compiler/src/dmd/frontend.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 6a451f5ae753..9f1e5a6e7295 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -6000,6 +6000,21 @@ enum class LintFlags : uint32_t all = 4294967295u, }; +struct TrackedParam final +{ + VarDeclaration* decl; + bool used; + TrackedParam() : + decl(), + used() + { + } + TrackedParam(VarDeclaration* decl, bool used = false) : + decl(decl), + used(used) + {} +}; + enum class ErrorPrintMode : uint8_t { simpleError = 0u, @@ -7921,12 +7936,14 @@ class LintVisitor final : public Visitor public: using Visitor::visit; _d_dynamicArray< LintFlags > flagsStack; - void* unusedTrack; + _d_dynamicArray< TrackedParam > activeParams; LintVisitor(); LintFlags currentFlags(); void visit(Dsymbol* s) override; void visit(Statement* s) override; void visit(Expression* e) override; + void visit(Initializer* i) override; + void visit(DeclarationExp* de) override; void visit(Module* m) override; void visit(AttribDeclaration* ad) override; void visit(PragmaDeclaration* pd) override; @@ -7943,6 +7960,9 @@ class LintVisitor final : public Visitor void visit(BinExp* e) override; void visit(UnaExp* e) override; void visit(CallExp* e) override; + void visit(VarDeclaration* vd) override; + void visit(ExpInitializer* ei) override; + void visit(FuncExp* fe) override; }; extern _d_real creall(complex_t x); From 9ae1569a36ccb6ba7f41c04953f06c87d36a1177 Mon Sep 17 00:00:00 2001 From: Kuzko Sergii Date: Tue, 21 Apr 2026 20:02:54 +0300 Subject: [PATCH 3/9] refactoring fix merge conflict --- compiler/src/dmd/frontend.h | 413 ++++++++++++++++-------------------- compiler/src/dmd/main.d | 6 +- 2 files changed, 189 insertions(+), 230 deletions(-) diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 9f1e5a6e7295..5232e2e80680 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -6,6 +6,7 @@ #include #include #include +#include #ifdef CUSTOM_D_ARRAY_TYPE #define _d_dynamicArray CUSTOM_D_ARRAY_TYPE @@ -274,11 +275,6 @@ class IdentityExp; class CondExp; class GenericExp; class DefaultInitExp; -class FileInitExp; -class LineInitExp; -class ModuleInitExp; -class FuncInitExp; -class PrettyFuncInitExp; class ObjcClassReferenceExp; class ClassReferenceExp; class ThrownExceptionExp; @@ -1400,11 +1396,6 @@ class ParseTimeVisitor virtual void visit(typename AST::ImportExp e); virtual void visit(typename AST::DotTemplateInstanceExp e); virtual void visit(typename AST::ArrayExp e); - virtual void visit(typename AST::FuncInitExp e); - virtual void visit(typename AST::PrettyFuncInitExp e); - virtual void visit(typename AST::FileInitExp e); - virtual void visit(typename AST::LineInitExp e); - virtual void visit(typename AST::ModuleInitExp e); virtual void visit(typename AST::CommaExp e); virtual void visit(typename AST::PostExp e); virtual void visit(typename AST::PowExp e); @@ -2247,25 +2238,20 @@ enum class EXP : uint8_t scope_ = 107u, traits = 108u, overloadSet = 109u, - line = 110u, - file = 111u, - fileFullPath = 112u, - moduleString = 113u, - functionString = 114u, - prettyFunction = 115u, - pow = 116u, - powAssign = 117u, - vector = 118u, - voidExpression = 119u, - cantExpression = 120u, - showCtfeContext = 121u, - objcClassReference = 122u, - vectorArray = 123u, - compoundLiteral = 124u, - _Generic_ = 125u, - interval = 126u, - loweredAssignExp = 127u, - rvalue = 128u, + defaultInit = 110u, + pow = 111u, + powAssign = 112u, + vector = 113u, + voidExpression = 114u, + cantExpression = 115u, + showCtfeContext = 116u, + objcClassReference = 117u, + vectorArray = 118u, + compoundLiteral = 119u, + _Generic_ = 120u, + interval = 121u, + loweredAssignExp = 122u, + rvalue = 123u, }; class Expression : public ASTNode @@ -2414,11 +2400,6 @@ class Expression : public ASTNode CondExp* isCondExp(); GenericExp* isGenericExp(); DefaultInitExp* isDefaultInitExp(); - FileInitExp* isFileInitExp(); - LineInitExp* isLineInitExp(); - ModuleInitExp* isModuleInitExp(); - FuncInitExp* isFuncInitExp(); - PrettyFuncInitExp* isPrettyFuncInitExp(); ObjcClassReferenceExp* isObjcClassReferenceExp(); ClassReferenceExp* isClassReferenceExp(); ThrownExceptionExp* isThrownExceptionExp(); @@ -2721,142 +2702,6 @@ class DeclarationExp final : public Expression void accept(Visitor* v) override; }; -class DefaultInitExp : public Expression -{ -public: - void accept(Visitor* v) override; -}; - -class DelegateExp final : public UnaExp -{ -public: - FuncDeclaration* func; - bool hasOverloads; - VarDeclaration* vthis2; - void accept(Visitor* v) override; -}; - -class DelegateFuncptrExp final : public UnaExp -{ -public: - void accept(Visitor* v) override; -}; - -class DelegatePtrExp final : public UnaExp -{ -public: - void accept(Visitor* v) override; -}; - -class DeleteExp final : public UnaExp -{ -public: - bool isRAII; - void accept(Visitor* v) override; -}; - -class DivAssignExp final : public BinAssignExp -{ -public: - void accept(Visitor* v) override; -}; - -class DivExp final : public BinExp -{ -public: - void accept(Visitor* v) override; -}; - -class IdentifierExp : public Expression -{ -public: - Identifier* ident; - static IdentifierExp* create(Loc loc, Identifier* ident); - void accept(Visitor* v) override; -}; - -class DollarExp final : public IdentifierExp -{ -public: - void accept(Visitor* v) override; -}; - -class DotExp final : public BinExp -{ -public: - void accept(Visitor* v) override; -}; - -class DotIdExp final : public UnaExp -{ -public: - Identifier* ident; - bool noderef; - bool wantsym; - bool arrow; - static DotIdExp* create(Loc loc, Expression* e, Identifier* ident); - void accept(Visitor* v) override; -}; - -class DotTemplateExp final : public UnaExp -{ -public: - TemplateDeclaration* td; - void accept(Visitor* v) override; -}; - -class DotTemplateInstanceExp final : public UnaExp -{ -public: - TemplateInstance* ti; - DotTemplateInstanceExp* syntaxCopy() override; - void accept(Visitor* v) override; -}; - -class DotTypeExp final : public UnaExp -{ -public: - Dsymbol* sym; - void accept(Visitor* v) override; -}; - -class DotVarExp final : public UnaExp -{ -public: - Declaration* var; - bool hasOverloads; - void accept(Visitor* v) override; -}; - -class DsymbolExp final : public Expression -{ -public: - Dsymbol* s; - bool hasOverloads; - void accept(Visitor* v) override; -}; - -class EqualExp final : public BinExp -{ -public: - Expression* lowering; - void accept(Visitor* v) override; -}; - -class ErrorExp final : public Expression -{ -public: - static ErrorExp* get(); - void accept(Visitor* v) override; - static ErrorExp* errorexp; -}; - -class FileInitExp final : public DefaultInitExp -{ -public: - void accept(Visitor* v) override; -}; - enum class TOK : uint8_t { reserved = 0u, @@ -3088,19 +2933,145 @@ enum class TOK : uint8_t __attribute___ = 226u, }; -class FuncExp final : public Expression +class DefaultInitExp : public Expression { public: - FuncLiteralDeclaration* fd; - TemplateDeclaration* td; TOK tok; - FuncExp* syntaxCopy() override; void accept(Visitor* v) override; + Expression* syntaxCopy() override; +}; + +class DelegateExp final : public UnaExp +{ +public: + FuncDeclaration* func; + bool hasOverloads; + VarDeclaration* vthis2; + void accept(Visitor* v) override; +}; + +class DelegateFuncptrExp final : public UnaExp +{ +public: + void accept(Visitor* v) override; +}; + +class DelegatePtrExp final : public UnaExp +{ +public: + void accept(Visitor* v) override; +}; + +class DeleteExp final : public UnaExp +{ +public: + bool isRAII; + void accept(Visitor* v) override; +}; + +class DivAssignExp final : public BinAssignExp +{ +public: + void accept(Visitor* v) override; +}; + +class DivExp final : public BinExp +{ +public: + void accept(Visitor* v) override; +}; + +class IdentifierExp : public Expression +{ +public: + Identifier* ident; + static IdentifierExp* create(Loc loc, Identifier* ident); + void accept(Visitor* v) override; +}; + +class DollarExp final : public IdentifierExp +{ +public: + void accept(Visitor* v) override; +}; + +class DotExp final : public BinExp +{ +public: + void accept(Visitor* v) override; +}; + +class DotIdExp final : public UnaExp +{ +public: + Identifier* ident; + bool noderef; + bool wantsym; + bool arrow; + static DotIdExp* create(Loc loc, Expression* e, Identifier* ident); + void accept(Visitor* v) override; +}; + +class DotTemplateExp final : public UnaExp +{ +public: + TemplateDeclaration* td; + void accept(Visitor* v) override; +}; + +class DotTemplateInstanceExp final : public UnaExp +{ +public: + TemplateInstance* ti; + DotTemplateInstanceExp* syntaxCopy() override; + void accept(Visitor* v) override; +}; + +class DotTypeExp final : public UnaExp +{ +public: + Dsymbol* sym; + void accept(Visitor* v) override; +}; + +class DotVarExp final : public UnaExp +{ +public: + Declaration* var; + bool hasOverloads; + void accept(Visitor* v) override; +}; + +class DsymbolExp final : public Expression +{ +public: + Dsymbol* s; + bool hasOverloads; + void accept(Visitor* v) override; +}; + +class EqualExp final : public BinExp +{ +public: + Expression* lowering; + void accept(Visitor* v) override; +}; + +class ErrorExp final : public Expression +{ +public: + static ErrorExp* get(); + void accept(Visitor* v) override; + static ErrorExp* errorexp; }; -class FuncInitExp final : public DefaultInitExp +class FuncExp final : public Expression { public: + FuncLiteralDeclaration* fd; + TemplateDeclaration* td; + TOK tok; + FuncExp* syntaxCopy() override; void accept(Visitor* v) override; }; @@ -3197,12 +3168,6 @@ class IsExp final : public Expression enum : bool { LOGSEMANTIC = false }; -class LineInitExp final : public DefaultInitExp -{ -public: - void accept(Visitor* v) override; -}; - class LogicalExp final : public BinExp { public: @@ -3248,12 +3213,6 @@ class ModExp final : public BinExp void accept(Visitor* v) override; }; -class ModuleInitExp final : public DefaultInitExp -{ -public: - void accept(Visitor* v) override; -}; - class MulAssignExp final : public BinAssignExp { public: @@ -3364,12 +3323,6 @@ class PreExp final : public UnaExp void accept(Visitor* v) override; }; -class PrettyFuncInitExp final : public DefaultInitExp -{ -public: - void accept(Visitor* v) override; -}; - class PtrExp final : public UnaExp { public: @@ -3492,6 +3445,17 @@ class StringExp final : public Expression class StructLiteralExp final : public Expression { public: + enum class StageFlags : uint8_t + { + none = 0u, + scrub = 1u, + searchPointers = 2u, + optimize = 4u, + apply = 8u, + inlineScan = 16u, + toCBuffer = 32u, + }; + struct BitFields final { bool useStaticInit; @@ -3529,17 +3493,6 @@ class StructLiteralExp final : public Expression StructLiteralExp* inlinecopy; }; StructLiteralExp* origin; - enum class StageFlags : uint8_t - { - none = 0u, - scrub = 1u, - searchPointers = 2u, - optimize = 4u, - apply = 8u, - inlineScan = 16u, - toCBuffer = 32u, - }; - static StructLiteralExp* create(Loc loc, StructDeclaration* sd, void* elements, Type* stype = nullptr); StructLiteralExp* syntaxCopy() override; void accept(Visitor* v) override; @@ -3985,10 +3938,6 @@ class NewDeclaration final : public FuncDeclaration struct ParameterDFAInfo final { - int32_t parameterId; - Fact notNullIn; - Fact notNullOut; - bool specifiedByUser; enum class Fact : uint8_t { Unspecified = 0u, @@ -3996,6 +3945,10 @@ struct ParameterDFAInfo final Guaranteed = 2u, }; + int32_t parameterId; + Fact notNullIn; + Fact notNullOut; + bool specifiedByUser; ParameterDFAInfo() : parameterId(), specifiedByUser() @@ -5280,6 +5233,7 @@ class TryCatchStatement final : public Statement Statement* _body; Array* catches; Statement* tryBody; + TOK loweredFromScopeGuard; TryCatchStatement* syntaxCopy() override; bool hasBreak() const override; void accept(Visitor* v) override; @@ -5292,6 +5246,8 @@ class TryFinallyStatement final : public Statement Statement* finalbody; Statement* tryBody; bool bodyFallsThru; + TOK loweredFromScopeGuard; + VarDeclaration* loweredFrom; static TryFinallyStatement* create(Loc loc, Statement* _body, Statement* finalbody); TryFinallyStatement* syntaxCopy() override; bool hasBreak() const override; @@ -5535,9 +5491,7 @@ struct ASTCodegen final using EqualExp = ::EqualExp; using ErrorExp = ::ErrorExp; using Expression = ::Expression; - using FileInitExp = ::FileInitExp; using FuncExp = ::FuncExp; - using FuncInitExp = ::FuncInitExp; using GenericExp = ::GenericExp; using HaltExp = ::HaltExp; using IdentifierExp = ::IdentifierExp; @@ -5549,7 +5503,6 @@ struct ASTCodegen final using InterpExp = ::InterpExp; using IntervalExp = ::IntervalExp; using IsExp = ::IsExp; - using LineInitExp = ::LineInitExp; using LogicalExp = ::LogicalExp; using LoweredAssignExp = ::LoweredAssignExp; using MemorySet = ::MemorySet; @@ -5558,7 +5511,6 @@ struct ASTCodegen final using MixinExp = ::MixinExp; using ModAssignExp = ::ModAssignExp; using ModExp = ::ModExp; - using ModuleInitExp = ::ModuleInitExp; using MulAssignExp = ::MulAssignExp; using MulExp = ::MulExp; using NegExp = ::NegExp; @@ -5575,7 +5527,6 @@ struct ASTCodegen final using PowAssignExp = ::PowAssignExp; using PowExp = ::PowExp; using PreExp = ::PreExp; - using PrettyFuncInitExp = ::PrettyFuncInitExp; using PtrExp = ::PtrExp; using RealExp = ::RealExp; using RemoveExp = ::RemoveExp; @@ -6184,6 +6135,7 @@ struct structalign_t final { PACK = 1u, ALIGNAS = 2u, + ALIGN_ATTRIB = 4u, }; public: @@ -6197,6 +6149,8 @@ struct structalign_t final void setPack(); bool fromAlignas() const; void setAlignas(); + bool fromCAlignAttribute() const; + void setCAlignAttribute(); structalign_t() : value(0u), flags() @@ -7007,6 +6961,7 @@ struct Scope final Dsymbol* parent; LabelStatement* slabel; SwitchStatement* switchStatement; + void* switchCases; Statement* tryBody; TryFinallyStatement* tryFinally; ScopeGuardStatement* scopeGuard; @@ -7014,6 +6969,7 @@ struct Scope final Statement* scontinue; ForeachStatement* fes; Scope* callsc; + Loc callLoc; Dsymbol* inunion; VarDeclaration* lastVar; ErrorSink* eSink; @@ -7056,6 +7012,8 @@ struct Scope final bool ctfeBlock(bool v); bool knownACompileTimeOnlyContext() const; bool knownACompileTimeOnlyContext(bool v); + bool inIsDisabledTrait() const; + bool inIsDisabledTrait(bool v); private: uint16_t bitFields; uint16_t bitFields2; @@ -7086,6 +7044,7 @@ struct Scope final parent(), slabel(), switchStatement(), + switchCases(), tryBody(), tryFinally(), scopeGuard(), @@ -7093,6 +7052,7 @@ struct Scope final scontinue(), fes(), callsc(), + callLoc(), inunion(), lastVar(), eSink(), @@ -7116,7 +7076,7 @@ struct Scope final argStruct() { } - Scope(Scope* enclosing, Module* _module = nullptr, ScopeDsymbol* scopesym = nullptr, FuncDeclaration* func = nullptr, VarDeclaration* varDecl = nullptr, Dsymbol* parent = nullptr, LabelStatement* slabel = nullptr, SwitchStatement* switchStatement = nullptr, Statement* tryBody = nullptr, TryFinallyStatement* tryFinally = nullptr, ScopeGuardStatement* scopeGuard = nullptr, Statement* sbreak = nullptr, Statement* scontinue = nullptr, ForeachStatement* fes = nullptr, Scope* callsc = nullptr, Dsymbol* inunion = nullptr, VarDeclaration* lastVar = nullptr, ErrorSink* eSink = nullptr, Module* minst = nullptr, TemplateInstance* tinst = nullptr, CtorFlow ctorflow = CtorFlow(), AlignDeclaration* aligndecl = nullptr, CPPNamespaceDeclaration* namespace_ = nullptr, LINK linkage = (LINK)1u, CPPMANGLE cppmangle = (CPPMANGLE)0u, PragmaDeclaration* inlining = nullptr, Visibility visibility = Visibility((Visibility::Kind)5u, nullptr), STC stc = (STC)0LLU, DeprecatedDeclaration* depdecl = nullptr, uint16_t bitFields = 0u, uint16_t bitFields2 = 0u, Previews previews = Previews(), UserAttributeDeclaration* userAttribDecl = nullptr, void* lastdc = nullptr, void* anchorCounts = nullptr, Identifier* prevAnchor = nullptr, AliasDeclaration* aliasAsg = nullptr, StructDeclaration* argStruct = nullptr) : + Scope(Scope* enclosing, Module* _module = nullptr, ScopeDsymbol* scopesym = nullptr, FuncDeclaration* func = nullptr, VarDeclaration* varDecl = nullptr, Dsymbol* parent = nullptr, LabelStatement* slabel = nullptr, SwitchStatement* switchStatement = nullptr, void* switchCases = nullptr, Statement* tryBody = nullptr, TryFinallyStatement* tryFinally = nullptr, ScopeGuardStatement* scopeGuard = nullptr, Statement* sbreak = nullptr, Statement* scontinue = nullptr, ForeachStatement* fes = nullptr, Scope* callsc = nullptr, Loc callLoc = Loc(), Dsymbol* inunion = nullptr, VarDeclaration* lastVar = nullptr, ErrorSink* eSink = nullptr, Module* minst = nullptr, TemplateInstance* tinst = nullptr, CtorFlow ctorflow = CtorFlow(), AlignDeclaration* aligndecl = nullptr, CPPNamespaceDeclaration* namespace_ = nullptr, LINK linkage = (LINK)1u, CPPMANGLE cppmangle = (CPPMANGLE)0u, PragmaDeclaration* inlining = nullptr, Visibility visibility = Visibility((Visibility::Kind)5u, nullptr), STC stc = (STC)0LLU, DeprecatedDeclaration* depdecl = nullptr, uint16_t bitFields = 0u, uint16_t bitFields2 = 0u, Previews previews = Previews(), UserAttributeDeclaration* userAttribDecl = nullptr, void* lastdc = nullptr, void* anchorCounts = nullptr, Identifier* prevAnchor = nullptr, AliasDeclaration* aliasAsg = nullptr, StructDeclaration* argStruct = nullptr) : enclosing(enclosing), _module(_module), scopesym(scopesym), @@ -7125,6 +7085,7 @@ struct Scope final parent(parent), slabel(slabel), switchStatement(switchStatement), + switchCases(switchCases), tryBody(tryBody), tryFinally(tryFinally), scopeGuard(scopeGuard), @@ -7132,6 +7093,7 @@ struct Scope final scontinue(scontinue), fes(fes), callsc(callsc), + callLoc(callLoc), inunion(inunion), lastVar(lastVar), eSink(eSink), @@ -7867,11 +7829,7 @@ class StrictVisitor : public ParseTimeVisitor virtual void visit(typename AST::ImportExp ) override; virtual void visit(typename AST::DotTemplateInstanceExp ) override; virtual void visit(typename AST::ArrayExp ) override; - virtual void visit(typename AST::FuncInitExp ) override; - virtual void visit(typename AST::PrettyFuncInitExp ) override; - virtual void visit(typename AST::FileInitExp ) override; - virtual void visit(typename AST::LineInitExp ) override; - virtual void visit(typename AST::ModuleInitExp ) override; + virtual void visit(typename AST::DefaultInitExp ) override; virtual void visit(typename AST::CommaExp ) override; virtual void visit(typename AST::PostExp ) override; virtual void visit(typename AST::PowExp ) override; @@ -7965,6 +7923,12 @@ class LintVisitor final : public Visitor void visit(FuncExp* fe) override; }; +class StatementWalker : public SemanticTimeTransitiveVisitor +{ +public: + void visit(Statement* st) final override; +}; + extern _d_real creall(complex_t x); extern _d_real cimagl(complex_t x); @@ -8749,17 +8713,11 @@ struct Id final static Identifier* _d_delThrowable; static Identifier* _d_newThrowable; static Identifier* _d_newclassT; - static Identifier* _d_newclassTTrace; static Identifier* _d_newitemT; - static Identifier* _d_newitemTTrace; static Identifier* _d_newarrayT; - static Identifier* _d_newarrayTTrace; static Identifier* _d_newarrayU; - static Identifier* _d_newarrayUTrace; static Identifier* _d_newarraymTX; - static Identifier* _d_newarraymTXTrace; static Identifier* _d_arrayliteralTX; - static Identifier* _d_arrayliteralTXTrace; static Identifier* _d_assert_fail; static Identifier* _d_arrayctor; static Identifier* _d_arraysetctor; @@ -8767,6 +8725,7 @@ struct Id final static Identifier* _d_arrayassign_l; static Identifier* _d_arrayassign_r; static Identifier* _d_cast; + static Identifier* _d_atomicOp; static Identifier* imported; static Identifier* InterpolationHeader; static Identifier* InterpolationFooter; @@ -8790,13 +8749,9 @@ struct Id final static Identifier* rt_init; static Identifier* _d_HookTraceImpl; static Identifier* _d_arraysetlengthT; - static Identifier* _d_arraysetlengthTTrace; static Identifier* _d_arrayappendT; - static Identifier* _d_arrayappendTTrace; static Identifier* _d_arrayappendcTX; - static Identifier* _d_arrayappendcTXTrace; static Identifier* _d_arraycatnTX; - static Identifier* _d_arraycatnTXTrace; static Identifier* _d_assocarrayliteralTX; static Identifier* stdc; static Identifier* stdarg; diff --git a/compiler/src/dmd/main.d b/compiler/src/dmd/main.d index 211696d0285d..836fd7607079 100644 --- a/compiler/src/dmd/main.d +++ b/compiler/src/dmd/main.d @@ -72,7 +72,6 @@ import dmd.target; import dmd.timetrace; import dmd.utils; import dmd.vsoptions; -import dmd.lint.engine : runLinter; /** * DMD's entry point, C main. @@ -668,6 +667,10 @@ private int tryMain(const(char)[][] argv, out Param params) runLinter(modules[]); + { + timeTraceBeginEvent(TimeTraceEventType.inlineGeneral); + scope (exit) timeTraceEndEvent(TimeTraceEventType.inlineGeneral); + // Scan for modules with always inline functions foreach (m; modules) { @@ -689,6 +692,7 @@ private int tryMain(const(char)[][] argv, out Param params) inlineScanAllFunctions(m, global.errorSink); } } + } if (global.warnings) errorOnWarning(); From db171ba2422831b19b4ce3e18e3f4084a31e6201 Mon Sep 17 00:00:00 2001 From: Kuzko Sergii Date: Tue, 21 Apr 2026 20:16:43 +0300 Subject: [PATCH 4/9] refactoring fix merge conflict --- compiler/src/dmd/frontend.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 5232e2e80680..e47188fdfbfa 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -7889,6 +7889,12 @@ class StrictVisitor : public ParseTimeVisitor virtual void visit(typename AST::CInitializer ) override; }; +class StatementWalker : public SemanticTimeTransitiveVisitor +{ +public: + void visit(Statement* st) final override; +}; + class LintVisitor final : public Visitor { public: @@ -7923,12 +7929,6 @@ class LintVisitor final : public Visitor void visit(FuncExp* fe) override; }; -class StatementWalker : public SemanticTimeTransitiveVisitor -{ -public: - void visit(Statement* st) final override; -}; - extern _d_real creall(complex_t x); extern _d_real cimagl(complex_t x); From 80a0705c466d27287e6114687c588302bdd35271 Mon Sep 17 00:00:00 2001 From: Kuzko Sergii Date: Tue, 21 Apr 2026 21:08:48 +0300 Subject: [PATCH 5/9] refactoring fix merge conflict --- compiler/src/dmd/main.d | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dmd/main.d b/compiler/src/dmd/main.d index 836fd7607079..d0e2d40b9134 100644 --- a/compiler/src/dmd/main.d +++ b/compiler/src/dmd/main.d @@ -53,6 +53,7 @@ import dmd.id; import dmd.identifier; import dmd.inline; import dmd.link; +import dmd.lint.engine : runLinter; import dmd.location; import dmd.mars; import dmd.mtype; From e1fafb09e8cc766434744d79f644255c6a98b268 Mon Sep 17 00:00:00 2001 From: Sergii Kuzko Date: Wed, 22 Apr 2026 17:58:46 +0300 Subject: [PATCH 6/9] add lint_struct_params --- compiler/test/compilable/lint_struct_params.d | 32 +++++++++++++++++++ .../lint_struct_params_fail.d | 30 +++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 compiler/test/compilable/lint_struct_params.d create mode 100644 compiler/test/fail_compilation/lint_struct_params_fail.d diff --git a/compiler/test/compilable/lint_struct_params.d b/compiler/test/compilable/lint_struct_params.d new file mode 100644 index 000000000000..27ac91ad92f7 --- /dev/null +++ b/compiler/test/compilable/lint_struct_params.d @@ -0,0 +1,32 @@ +/* +REQUIRED_ARGS: -w +TEST_OUTPUT: +---- +---- +*/ + +struct LintParams { + bool enabled = true; + bool constSpecial = true; + bool unusedParams = true; +} + +enum MyLintParams = LintParams(true, false, false); + +pragma(lint, MyLintParams); + +struct TestStruct +{ + bool opEquals(ref const TestStruct other) { return true; } +} + +class A +{ + void foo(int x) + { + } +} + +void main() +{ +} \ No newline at end of file diff --git a/compiler/test/fail_compilation/lint_struct_params_fail.d b/compiler/test/fail_compilation/lint_struct_params_fail.d new file mode 100644 index 000000000000..3cc00afd6d22 --- /dev/null +++ b/compiler/test/fail_compilation/lint_struct_params_fail.d @@ -0,0 +1,30 @@ +/* +REQUIRED_ARGS: -w +TEST_OUTPUT: +--- +fail_compilation/lint_struct_params_fail.d(24): Warning: [constSpecial] special method `opEquals` should be marked as `const` +fail_compilation/lint_struct_params_fail.d(29): Warning: [unusedParams] function parameter `x` is never used +Error: warnings are treated as errors + Use -wi if you wish to treat warnings only as informational. +--- +*/ + +struct LintParams { + bool enabled = true; + bool constSpecial = true; + bool unusedParams = true; +} + +enum StrictLint = LintParams(true, true, true); +pragma(lint, StrictLint): + +struct BadStruct +{ + // LINT: constSpecial + bool opEquals(ref const BadStruct _rhs) { return true; } +} + +class A +{ + final void foo(int x) {} +} \ No newline at end of file From 933946c519340696730be31e0f420b945f9576cd Mon Sep 17 00:00:00 2001 From: Sergii Kuzko Date: Wed, 22 Apr 2026 18:14:51 +0300 Subject: [PATCH 7/9] add lint_struct_params --- compiler/src/dmd/id.d | 2 + compiler/src/dmd/lint/engine.d | 87 ++++++++++++++++--- compiler/src/dmd/pragmasem.d | 45 +++++++++- compiler/test/compilable/lint_struct_params.d | 2 +- .../lint_struct_params_fail.d | 2 +- 5 files changed, 118 insertions(+), 20 deletions(-) diff --git a/compiler/src/dmd/id.d b/compiler/src/dmd/id.d index a105f6654392..94187595f2fe 100644 --- a/compiler/src/dmd/id.d +++ b/compiler/src/dmd/id.d @@ -543,6 +543,8 @@ immutable Msgtable[] msgtable = { "lint" }, { "constSpecial" }, { "unusedParams" }, + { "LintParams" }, + { "enabled" }, { "none" }, { "all" }, diff --git a/compiler/src/dmd/lint/engine.d b/compiler/src/dmd/lint/engine.d index 9a799992d006..bbbbb3f857ad 100644 --- a/compiler/src/dmd/lint/engine.d +++ b/compiler/src/dmd/lint/engine.d @@ -213,27 +213,86 @@ extern(C++) final class LintVisitor : Visitor private LintFlags parsePragmaArgs(Expressions* args) { + import core.stdc.string : strcmp; + LintFlags newFlags = currentFlags(); if (!args || args.length == 0) { newFlags |= LintFlags.all; + return newFlags; } - else + + foreach (arg; *args) { - foreach (arg; *args) + if (!arg) continue; + + if (auto ve = arg.isVarExp()) { - if (!arg) continue; - auto id = arg.isIdentifierExp(); - if (!id) continue; - - if (id.ident == Id.constSpecial) - newFlags |= LintFlags.constSpecial; - else if (id.ident == Id.unusedParams) - newFlags |= LintFlags.unusedParams; - else if (id.ident == Id.none) - newFlags = LintFlags.none; - else if (id.ident == Id.all) - newFlags |= LintFlags.all; + if (ve.var && ve.var.isVarDeclaration() && ve.var.isVarDeclaration()._init) + { + if (auto ei = ve.var.isVarDeclaration()._init.isExpInitializer()) + arg = ei.exp; + } + } + + if (auto id = arg.isIdentifierExp()) + { + if (id.ident == Id.constSpecial) newFlags |= LintFlags.constSpecial; + else if (id.ident == Id.unusedParams) newFlags |= LintFlags.unusedParams; + else if (id.ident == Id.none) newFlags = LintFlags.none; + else if (id.ident == Id.all) newFlags |= LintFlags.all; + } + + else if (auto sle = arg.isStructLiteralExp()) + { + if (sle.sd && sle.sd.ident && strcmp(sle.sd.ident.toChars(), "LintParams") == 0) + { + bool masterEnabled = true; + LintFlags tempFlags = newFlags; + + for (size_t i = 0; i < sle.elements.length; i++) + { + Expression e = (*sle.elements)[i]; + if (!e) continue; + + VarDeclaration field = sle.sd.fields[i]; + if (!field || !field.ident) continue; + + bool isFieldEnabled = false; + + if (auto ie = e.isIntegerExp()) + { + isFieldEnabled = ie.getInteger() != 0; + } + else if (auto ce = e.isCastExp()) + { + if (auto ce_ie = ce.e1.isIntegerExp()) + isFieldEnabled = ce_ie.getInteger() != 0; + } + + const(char)* fieldName = field.ident.toChars(); + + if (strcmp(fieldName, "enabled") == 0) + { + masterEnabled = isFieldEnabled; + } + else if (strcmp(fieldName, "constSpecial") == 0) + { + if (isFieldEnabled) tempFlags |= LintFlags.constSpecial; + else tempFlags &= ~LintFlags.constSpecial; + } + else if (strcmp(fieldName, "unusedParams") == 0) + { + if (isFieldEnabled) tempFlags |= LintFlags.unusedParams; + else tempFlags &= ~LintFlags.unusedParams; + } + } + + if (!masterEnabled) + newFlags = LintFlags.none; + else + newFlags = tempFlags; + } } } return newFlags; diff --git a/compiler/src/dmd/pragmasem.d b/compiler/src/dmd/pragmasem.d index cf20413d89ec..097ea320fa32 100644 --- a/compiler/src/dmd/pragmasem.d +++ b/compiler/src/dmd/pragmasem.d @@ -207,9 +207,28 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc) return declarations(); } else if (pd.ident == Id.lint) - { - return declarations(); - } + { + if (pd.args) + { + for (size_t i = 0; i < pd.args.length; i++) + { + Expression e = (*pd.args)[i]; + if (auto id = e.isIdentifierExp()) + { + if (id.ident == Id.constSpecial || id.ident == Id.unusedParams || + id.ident == Id.none || id.ident == Id.all) + { + continue; + } + } + + e = e.expressionSemantic(sc); + e = e.ctfeInterpret(); + (*pd.args)[i] = e; + } + } + return declarations(); + } else if (!global.params.ignoreUnsupportedPragmas) { error(pd.loc, "unrecognized `pragma(%s)`", pd.ident.toErrMsg()); @@ -335,7 +354,25 @@ bool pragmaStmtSemantic(PragmaStatement ps, Scope* sc) } else if (ps.ident == Id.lint) { - // Linter will process this later. We accept it as a valid pragma. + if (ps.args) + { + for (size_t i = 0; i < ps.args.length; i++) + { + Expression e = (*ps.args)[i]; + if (auto id = e.isIdentifierExp()) + { + if (id.ident == Id.constSpecial || id.ident == Id.unusedParams || + id.ident == Id.none || id.ident == Id.all) + { + continue; + } + } + + e = e.expressionSemantic(sc); + e = e.ctfeInterpret(); + (*ps.args)[i] = e; + } + } } else if (!global.params.ignoreUnsupportedPragmas) { diff --git a/compiler/test/compilable/lint_struct_params.d b/compiler/test/compilable/lint_struct_params.d index 27ac91ad92f7..d7096c5e2054 100644 --- a/compiler/test/compilable/lint_struct_params.d +++ b/compiler/test/compilable/lint_struct_params.d @@ -29,4 +29,4 @@ class A void main() { -} \ No newline at end of file +} diff --git a/compiler/test/fail_compilation/lint_struct_params_fail.d b/compiler/test/fail_compilation/lint_struct_params_fail.d index 3cc00afd6d22..ca86a34e0b86 100644 --- a/compiler/test/fail_compilation/lint_struct_params_fail.d +++ b/compiler/test/fail_compilation/lint_struct_params_fail.d @@ -27,4 +27,4 @@ struct BadStruct class A { final void foo(int x) {} -} \ No newline at end of file +} From 8ea2d40199ee6d98161fb0e053adb5eea0eeefdb Mon Sep 17 00:00:00 2001 From: Sergii Kuzko Date: Wed, 22 Apr 2026 19:01:37 +0300 Subject: [PATCH 8/9] fix compiler/src/dmd/frontend.h --- compiler/src/dmd/frontend.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index e47188fdfbfa..ebb46e8c6ea5 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -8932,6 +8932,8 @@ struct Id final static Identifier* lint; static Identifier* constSpecial; static Identifier* unusedParams; + static Identifier* LintParams; + static Identifier* enabled; static Identifier* none; static Identifier* all; static void initialize(); From ac1ef574764e3588c3c2ee729f80c609bfa2841c Mon Sep 17 00:00:00 2001 From: Sergii Kuzko Date: Wed, 22 Apr 2026 19:34:18 +0300 Subject: [PATCH 9/9] fix windows error func() -> func --- compiler/src/dmd/lint/engine.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dmd/lint/engine.d b/compiler/src/dmd/lint/engine.d index bbbbb3f857ad..c3249d2f8184 100644 --- a/compiler/src/dmd/lint/engine.d +++ b/compiler/src/dmd/lint/engine.d @@ -188,7 +188,7 @@ extern(C++) final class LintVisitor : Visitor private void checkConstSpecial(FuncDeclaration fd) { - if (fd.isGenerated() || (fd.storage_class & STC.const_) || (fd.type && fd.type.isConst())) + if (fd.isGenerated || (fd.storage_class & STC.const_) || (fd.type && fd.type.isConst())) return; if (fd.ident != Id.opEquals && fd.ident != Id.opCmp &&