Adjust global argument freezing (#41305)
David Bennett committed
Aug 10, 2026 at 17:52 UTC
fae0433930016b01f57c892d076583e37dff53fc
7 files changed
+55
-14
src/windows/wslc/arguments/ArgMap.h
+11
-1
@@ -117,6 +117,11 @@ public:
117
return m_validated.count(type);
118
}
119
120
+ bool IsValidated(ArgType type) const
121
+ {
122
+ return m_validatedTypes.count(type) != 0;
123
+ }
124
+
125
// Drops `type`'s memoized validation state (converted cache and validated record) so it never
126
// outlives the raw data.
127
void InvalidateValidated(ArgType type)
@@ -128,6 +133,11 @@ public:
133
// Records `type` as validated for its current raw values so reads skip re-validation.
134
void MarkValidated(ArgType type)
135
{
136
+ if (IsValidated(type))
137
+ {
138
+ return;
139
+ }
140
+
141
ThrowIfImmutable(type, "mark the argument as validated");
142
m_validatedTypes.insert(type);
143
}
@@ -231,7 +241,7 @@ private:
241
// demand, and its errors reported, exactly like a command-line value.
242
void EnsureValidated(ArgType type)
243
{
234
- if (m_validatedTypes.count(type) != 0)
244
+ if (IsValidated(type))
245
{
246
return;
247
}
src/windows/wslc/arguments/ArgumentValidation.cpp
+5
@@ -72,6 +72,11 @@ namespace {
72
// validated on success.
73
void Argument::Validate(ArgMap& execArgs) const
74
{
75
+ if (execArgs.IsValidated(m_argType))
76
+ {
77
+ return;
78
+ }
79
+
80
switch (m_argType)
81
{
82
case ArgType::BuildLabel:
src/windows/wslc/core/CLIExecutionContext.cpp
+4
-6
@@ -16,13 +16,11 @@ HANDLE CLIExecutionContext::CreateCancelEvent()
16
return CancelEvent.get();
17
}
18
19
-// This method should be idempotent.
20
-void CLIExecutionContext::ApplyGlobalOptions()
19
+void CLIExecutionContext::ApplyGlobalEnvironmentOptions()
20
{
22
- if (GlobalArgs.GetValue<ArgType::NoColor>())
23
- {
24
- Terminal.SetNoColor(true);
25
- }
21
+ // NoColor is environment-only and resolved before any output. Freezing it keeps the terminal
22
+ // color state consistent for the entire invocation.
23
+ Terminal.SetNoColor(GlobalArgs.GetValue<ArgType::NoColor>());
24
}
25
26
} // namespace wsl::windows::wslc::execution
src/windows/wslc/core/CLIExecutionContext.h
+2
-3
@@ -50,9 +50,8 @@ struct CLIExecutionContext : public wsl::windows::common::ExecutionContext
50
51
HANDLE CreateCancelEvent();
52
53
- // Single chokepoint that turns parsed GlobalArgs into process-wide effects
54
- // (debug logging, VT color, ...). Idempotent.
55
- void ApplyGlobalOptions();
53
+ // Applies and freezes environment-only global options before command-line parsing reports errors.
54
+ void ApplyGlobalEnvironmentOptions();
55
};
56
57
} // namespace wsl::windows::wslc::execution
src/windows/wslc/core/Main.cpp
+2
-3
@@ -92,7 +92,7 @@ try
92
// throw can't reroute through the colored-help error path.
93
auto envDefs = command->GetGlobalsAndEnvArguments();
94
ApplyEnvironmentOptions(context.GlobalArgs, envDefs);
95
- context.ApplyGlobalOptions();
95
+ context.ApplyGlobalEnvironmentOptions();
96
97
// Past this point, environment variable options are in effect.
98
@@ -119,9 +119,8 @@ try
119
/*stopOnUnknown*/ true,
120
/*overridableDefaults*/ envDefs);
121
command->ValidateArguments(context.GlobalArgs, envDefs, /*runInternalHook*/ false);
122
- context.ApplyGlobalOptions();
122
124
- // Past this point, global options are in effect.
123
+ // Past this point, global option parsing and validation are complete.
124
125
// Pass 2 - Subcommand and leaf command resolution.
126
std::unique_ptr<Command> subCommand = command->FindSubCommand(invocation);
test/windows/wslc/WSLCCLIArgumentUnitTests.cpp
+5
-1
@@ -627,6 +627,11 @@ class WSLCCLIArgumentUnitTests
627
ArgMap args;
628
args.Add(ArgType::Signal, std::wstring(L"SIGTERM"));
629
VERIFY_ARE_EQUAL(args.GetValue<ArgType::Signal>(), WSLCSignalSIGTERM);
630
+ VERIFY_ARE_EQUAL(args.CountValidated(ArgType::Signal), static_cast<size_t>(1));
631
+
632
+ Argument::Create(ArgType::Signal).Validate(args);
633
+ args.MarkValidated(ArgType::Signal);
634
+ VERIFY_ARE_EQUAL(args.CountValidated(ArgType::Signal), static_cast<size_t>(1));
635
636
const auto verifyImmutableFailure = [](const auto& operation) {
637
VERIFY_THROWS_SPECIFIC(operation(), wil::ResultException, [](const wil::ResultException& e) {
@@ -638,7 +643,6 @@ class WSLCCLIArgumentUnitTests
643
verifyImmutableFailure([&] { args.Remove(ArgType::Signal); });
644
verifyImmutableFailure([&] { args.InvalidateValidated(ArgType::Signal); });
645
verifyImmutableFailure([&] { args.AddValidated<ArgType::Signal>(WSLCSignalSIGKILL); });
641
- verifyImmutableFailure([&] { args.MarkValidated(ArgType::Signal); });
646
647
// Immutability is per argument; other arguments remain writable until they are read.
648
args.Add(ArgType::StopTimeout, std::wstring(L"30"));
test/windows/wslc/WSLCCLIExecutionUnitTests.cpp
+26
@@ -53,6 +53,32 @@ class WSLCCLIExecutionUnitTests
53
return true;
54
}
55
56
+ TEST_METHOD(GlobalEnvironmentOptions_NoColorIsAppliedAndFrozen)
57
+ {
58
+ {
59
+ CLIExecutionContext context;
60
+
61
+ context.ApplyGlobalEnvironmentOptions();
62
+ VERIFY_IS_FALSE(context.Terminal.IsNoColor());
63
+
64
+ VERIFY_THROWS_SPECIFIC(context.GlobalArgs.Add<ArgType::NoColor>(true), wil::ResultException, [](const wil::ResultException& e) {
65
+ return e.GetErrorCode() == E_ILLEGAL_METHOD_CALL;
66
+ });
67
+ }
68
+
69
+ {
70
+ CLIExecutionContext present;
71
+ present.GlobalArgs.Add<ArgType::NoColor>(true);
72
+ present.ApplyGlobalEnvironmentOptions();
73
+ VERIFY_IS_TRUE(present.Terminal.IsNoColor());
74
+
75
+ VERIFY_NO_THROW(Argument::Create(ArgType::NoColor).Validate(present.GlobalArgs));
76
+ VERIFY_THROWS_SPECIFIC(present.GlobalArgs.Remove(ArgType::NoColor), wil::ResultException, [](const wil::ResultException& e) {
77
+ return e.GetErrorCode() == E_ILLEGAL_METHOD_CALL;
78
+ });
79
+ }
80
+ }
81
+
82
// Test: Verify EnumVariantMap on DataMap for Context Data
83
TEST_METHOD(EnumVariantMap_DataMapValidation)
84
{