From aa7ede1a8dc0332164ed71c49a016915468c3a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 10 Apr 2026 03:17:58 +0200 Subject: [PATCH] Add new flag -unittest-roots --- compiler/include/dmd/globals.h | 1 + compiler/src/dmd/cli.d | 5 +++++ compiler/src/dmd/dmodule.d | 2 +- compiler/src/dmd/dsymbolsem.d | 14 +++++++++++--- compiler/src/dmd/expressionsem.d | 4 ++-- compiler/src/dmd/globals.d | 5 +++-- compiler/src/dmd/iasm/dmdaarch64.d | 2 +- compiler/src/dmd/iasm/gcc.d | 2 +- compiler/src/dmd/mars.d | 5 +++++ compiler/src/dmd/statementsem.d | 2 +- compiler/src/dmd/typesem.d | 2 +- 11 files changed, 32 insertions(+), 12 deletions(-) diff --git a/compiler/include/dmd/globals.h b/compiler/include/dmd/globals.h index 84142e5d6774..02805057e678 100644 --- a/compiler/include/dmd/globals.h +++ b/compiler/include/dmd/globals.h @@ -187,6 +187,7 @@ struct Param d_bool tracegc; // instrument calls to 'new' d_bool vcg_ast; // write-out codegen-ast d_bool useUnitTests; // generate unittest code + d_bool useUnitTestsRootOnly; // generate unittest code for root modules only d_bool useInline; // inline expand functions d_bool release; // build release version d_bool preservePaths; // true means don't strip path from source file diff --git a/compiler/src/dmd/cli.d b/compiler/src/dmd/cli.d index 42d400396947..f3dc8d02baba 100644 --- a/compiler/src/dmd/cli.d +++ b/compiler/src/dmd/cli.d @@ -933,6 +933,11 @@ dmd -cov -unittest myprog.d `Compile in $(LINK2 spec/unittest.html, unittest) code, turns on asserts, and sets the $(D unittest) $(LINK2 spec/version.html#PredefinedVersions, version identifier)`, ), + Option("unittest-roots", + "compile in unit tests for root modules only", + `Compile in $(LINK2 spec/unittest.html, unittest) code, turns on asserts, and sets the + $(D unittest) $(LINK2 spec/version.html#PredefinedVersions, version identifier) only for root modules whose sources files are explicitly passed as arguments to compiler`, + ), Option("v", "verbose", `Enable verbose output for each compiler pass`, diff --git a/compiler/src/dmd/dmodule.d b/compiler/src/dmd/dmodule.d index ff8dc27e5fa1..d400298a6e32 100644 --- a/compiler/src/dmd/dmodule.d +++ b/compiler/src/dmd/dmodule.d @@ -767,7 +767,7 @@ extern (C++) final class Module : Package } else { - const bool doUnittests = global.params.parsingUnittestsRequired(); + const bool doUnittests = global.params.parsingUnittestsRequired(this.isRoot); scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests); p.nextToken(); p.parseModuleDeclaration(); diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index d9a9df6d05c0..4e8598ed92d2 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -3804,8 +3804,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - const bool doUnittests = global.params.parsingUnittestsRequired(); - scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + auto mod = sc._module; + const bool doUnittests = global.params.parsingUnittestsRequired(mod.isRoot); + scope p = new Parser!ASTCodegen(mod, str, false, global.errorSink, &global.compileEnv, doUnittests); adjustLocForMixin(str, cd.loc, *p.baseLoc, global.params.mixinOut); p.linnum = p.baseLoc.startLine; p.nextToken(); @@ -4769,7 +4770,14 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor return; } - if (global.params.useUnitTests) + auto use = global.params.useUnitTests; + if (use && global.params.useUnitTestsRootOnly) + { + auto m = sc._module; + if (m && !m.isRoot()) + use = false; + } + if (use) { if (!utd.type) utd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, utd.storage_class); diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 3bf47b420f4c..eee6f0303159 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -5880,7 +5880,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { arguments.push(makeTemplateItem(Id.InterpolatedExpression, str)); const parseErrors = global.errors; - const bool doUnittests = global.params.parsingUnittestsRequired(); + const bool doUnittests = global.params.parsingUnittestsRequired(sc._module.isRoot); auto exprSl = SourceLoc(e.interpolatedSet.locs[idx]); scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.baseLoc.startLine = exprSl.line; @@ -9394,7 +9394,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor const errors = global.errors; const len = buf.length; const str = buf.extractChars()[0 .. len]; - const bool doUnittests = global.params.parsingUnittestsRequired(); + const bool doUnittests = global.params.parsingUnittestsRequired(sc._module.isRoot); scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); adjustLocForMixin(str, exp.loc, *p.baseLoc, global.params.mixinOut); p.linnum = p.baseLoc.startLine; diff --git a/compiler/src/dmd/globals.d b/compiler/src/dmd/globals.d index 0c4f9eb5417c..76c47b21d501 100644 --- a/compiler/src/dmd/globals.d +++ b/compiler/src/dmd/globals.d @@ -163,6 +163,7 @@ extern (C++) struct Param bool tracegc; // instrument calls to 'new' bool vcg_ast; // write-out codegen-ast bool useUnitTests; // generate unittest code + bool useUnitTestsRootOnly; // generate unittest code for root modules only bool useInline = false; // inline expand functions bool release; // build release version bool preservePaths; // true means don't strip path from source file @@ -274,9 +275,9 @@ extern (C++) struct Param const(char)* timeTraceFile; /// File path of output file /// - bool parsingUnittestsRequired() @safe + bool parsingUnittestsRequired(bool isRoot) @safe { - return useUnitTests || ddoc.doOutput || dihdr.doOutput; + return (useUnitTests && (!useUnitTestsRootOnly || isRoot)) || ddoc.doOutput || dihdr.doOutput; } } diff --git a/compiler/src/dmd/iasm/dmdaarch64.d b/compiler/src/dmd/iasm/dmdaarch64.d index 275bfb2abdcb..fc8ace59557b 100644 --- a/compiler/src/dmd/iasm/dmdaarch64.d +++ b/compiler/src/dmd/iasm/dmdaarch64.d @@ -72,7 +72,7 @@ public Statement inlineAsmAArch64Semantic(InlineAsmStatement s, Scope* sc) } } - const bool doUnittests = global.params.parsingUnittestsRequired(); + const bool doUnittests = global.params.parsingUnittestsRequired(sc._module.isRoot); scope p = new Parser!ASTCodegen(sc._module, "", false, global.errorSink, &global.compileEnv, doUnittests); /* Set list of tokens that will be the input to the parser, and starting line number to use. diff --git a/compiler/src/dmd/iasm/gcc.d b/compiler/src/dmd/iasm/gcc.d index 2f5d7aaf9f25..df9ba4130381 100644 --- a/compiler/src/dmd/iasm/gcc.d +++ b/compiler/src/dmd/iasm/gcc.d @@ -43,7 +43,7 @@ import dmd.typesem; public Statement gccAsmSemantic(GccAsmStatement s, Scope* sc) { //printf("GccAsmStatement.semantic()\n"); - const bool doUnittests = global.params.parsingUnittestsRequired(); + const bool doUnittests = global.params.parsingUnittestsRequired(sc._module.isRoot); scope p = (sc && sc.inCfile) ? new CParser!ASTCodegen(sc._module, "", false, global.errorSink, target.c, null, &global.compileEnv) : new Parser!ASTCodegen(sc._module, "", false, global.errorSink, &global.compileEnv, doUnittests); diff --git a/compiler/src/dmd/mars.d b/compiler/src/dmd/mars.d index d2dbf640be15..e6a1af29ee60 100644 --- a/compiler/src/dmd/mars.d +++ b/compiler/src/dmd/mars.d @@ -1660,6 +1660,11 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, out Param } else if (arg == "-unittest") params.useUnitTests = true; + else if (arg == "-unittest-roots") + { + params.useUnitTests = true; + params.useUnitTestsRootOnly = true; + } else if (p[1] == 'I') // https://dlang.org/dmd.html#switch-I { params.imppath.push(ImportPathInfo(p + 2 + (p[2] == '='))); diff --git a/compiler/src/dmd/statementsem.d b/compiler/src/dmd/statementsem.d index dbaf9023f8b1..14a87a0050da 100644 --- a/compiler/src/dmd/statementsem.d +++ b/compiler/src/dmd/statementsem.d @@ -5001,7 +5001,7 @@ private Statements* flatten(Statement statement, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - const bool doUnittests = global.params.parsingUnittestsRequired(); + const bool doUnittests = global.params.parsingUnittestsRequired(sc._module.isRoot); scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); adjustLocForMixin(str, cs.loc, *p.baseLoc, global.params.mixinOut); p.linnum = p.baseLoc.startLine; diff --git a/compiler/src/dmd/typesem.d b/compiler/src/dmd/typesem.d index 62d13b92d3c8..25e75edf2f87 100644 --- a/compiler/src/dmd/typesem.d +++ b/compiler/src/dmd/typesem.d @@ -9660,7 +9660,7 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - const bool doUnittests = global.params.parsingUnittestsRequired(); + const bool doUnittests = global.params.parsingUnittestsRequired(sc._module.isRoot); scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); adjustLocForMixin(str, loc, *p.baseLoc, global.params.mixinOut); p.linnum = p.baseLoc.startLine;