diff --git a/compiler/src/dmd/cli.d b/compiler/src/dmd/cli.d index 5cd376e27f6c..dc2285174fab 100644 --- a/compiler/src/dmd/cli.d +++ b/compiler/src/dmd/cli.d @@ -913,6 +913,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 40d9736caf7f..842ba4cbfb87 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 36ce6ce40a7c..bb0f9316bd84 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -3764,8 +3764,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(); @@ -4719,7 +4720,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 04cba0aac19e..d90bc5776356 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -9345,7 +9345,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/frontend.h b/compiler/src/dmd/frontend.h index 48bcac12570e..9fb047817dbb 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -8167,6 +8167,7 @@ struct Param final bool vcg_ast; DiagnosticReporting useDeprecated; bool useUnitTests; + bool useUnitTestsRootOnly; bool useInline; bool release; bool preservePaths; @@ -8250,7 +8251,7 @@ struct Param final bool timeTrace; uint32_t timeTraceGranularityUs; const char* timeTraceFile; - bool parsingUnittestsRequired(); + bool parsingUnittestsRequired(bool isRoot); Param() : obj(true), readStdin(), @@ -8260,6 +8261,7 @@ struct Param final vcg_ast(), useDeprecated((DiagnosticReporting)1u), useUnitTests(), + useUnitTestsRootOnly(), useInline(false), release(), preservePaths(), @@ -8335,7 +8337,7 @@ struct Param final timeTraceFile() { } - Param(bool obj, bool readStdin = false, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting useWarnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = true, bool rewriteNoExceptionToSeq = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), Edition edition = (Edition)2023u, void* editionFiles = nullptr, FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState safer = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, bool useFastDFA = false, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useNullCheck = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), bool debugEnabled = false, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}, bool fullyQualifiedObjectFiles = false, bool timeTrace = false, uint32_t timeTraceGranularityUs = 500u, const char* timeTraceFile = nullptr) : + Param(bool obj, bool readStdin = false, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useUnitTestsRootOnly = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting useWarnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = true, bool rewriteNoExceptionToSeq = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), Edition edition = (Edition)2023u, void* editionFiles = nullptr, FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState safer = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, bool useFastDFA = false, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useNullCheck = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), bool debugEnabled = false, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}, bool fullyQualifiedObjectFiles = false, bool timeTrace = false, uint32_t timeTraceGranularityUs = 500u, const char* timeTraceFile = nullptr) : obj(obj), readStdin(readStdin), multiobj(multiobj), @@ -8344,6 +8346,7 @@ struct Param final vcg_ast(vcg_ast), useDeprecated(useDeprecated), useUnitTests(useUnitTests), + useUnitTestsRootOnly(useUnitTestsRootOnly), useInline(useInline), release(release), preservePaths(preservePaths), diff --git a/compiler/src/dmd/globals.d b/compiler/src/dmd/globals.d index 020d24786cb6..3486c256d21b 100644 --- a/compiler/src/dmd/globals.d +++ b/compiler/src/dmd/globals.d @@ -165,6 +165,7 @@ extern (C++) struct Param bool vcg_ast; // write-out codegen-ast DiagnosticReporting useDeprecated = DiagnosticReporting.inform; // how use of deprecated features are handled 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 @@ -277,9 +278,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/globals.h b/compiler/src/dmd/globals.h index 2eb7779f6279..601263d8cf96 100644 --- a/compiler/src/dmd/globals.h +++ b/compiler/src/dmd/globals.h @@ -189,6 +189,7 @@ struct Param d_bool vcg_ast; // write-out codegen-ast Diagnostic useDeprecated; 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/iasm/dmdaarch64.d b/compiler/src/dmd/iasm/dmdaarch64.d index cfcef47f9784..75f9e9a305a0 100644 --- a/compiler/src/dmd/iasm/dmdaarch64.d +++ b/compiler/src/dmd/iasm/dmdaarch64.d @@ -73,7 +73,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 9841382ad37d..920f20296f7d 100644 --- a/compiler/src/dmd/mars.d +++ b/compiler/src/dmd/mars.d @@ -1659,6 +1659,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 bb56a832c209..fa0f2aff7642 100644 --- a/compiler/src/dmd/statementsem.d +++ b/compiler/src/dmd/statementsem.d @@ -4965,7 +4965,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 3f20232240b5..b3c740e6b3c6 100644 --- a/compiler/src/dmd/typesem.d +++ b/compiler/src/dmd/typesem.d @@ -9646,7 +9646,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;