From 2e550ffbb5713e3e19891d44051f7ba8dd228fbe Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Sun, 9 Aug 2026 20:01:49 +0000 Subject: [PATCH] Correct PowerShell authored command completeness --- IMPLEMENTATION_PLAN.md | 24 ++-- .../specs/bounded-shell-analysis/spec.md | 7 +- .../Pwsh/Parsing/PwshCommandParser.cs | 7 + .../Pwsh/Parsing/PwshForEachValueAnalysis.cs | 134 ++++++------------ src/ShellSyntaxTree/PwshParserOptions.cs | 11 +- .../powershell/001_simple_get_date.json | 2 +- .../063_compound_three_semicolon.json | 18 +-- .../068_compound_andif_then_semicolon.json | 9 +- .../powershell/072_setloc_cd_then_git.json | 7 +- .../073_setloc_setlocation_then_gci.json | 7 +- .../Corpus/powershell/074_setloc_two_cd.json | 14 +- .../powershell/077_setloc_path_param.json | 10 +- .../powershell/078_setloc_through_group.json | 7 +- .../Corpus/powershell/080_setloc_no_arg.json | 7 +- .../081_setloc_relative_after_literal.json | 17 ++- .../Corpus/powershell/084_setloc_andif.json | 7 +- .../Corpus/powershell/128_redirect_out.json | 2 +- .../powershell/129_redirect_append.json | 2 +- .../Corpus/powershell/130_redirect_err.json | 2 +- .../powershell/131_redirect_err_append.json | 2 +- .../powershell/132_redirect_verbose.json | 2 +- .../powershell/133_redirect_warning.json | 2 +- .../Corpus/powershell/134_redirect_debug.json | 2 +- .../powershell/135_redirect_information.json | 2 +- .../Corpus/powershell/136_redirect_all.json | 2 +- .../powershell/137_redirect_all_append.json | 2 +- .../powershell/138_redirect_merge_2to1.json | 2 +- .../powershell/139_redirect_merge_3to1.json | 2 +- .../143_recursion_command_pipeline.json | 28 +++- .../148_recursion_command_setlocation.json | 7 +- .../152_recursion_encoded_pipeline.json | 26 +++- .../162_dynamic_call_scriptblock.json | 2 +- .../165_dynamic_subexpression_arg.json | 4 +- .../powershell/226_iex_inherits_location.json | 10 +- .../powershell/227_iex_exports_location.json | 10 +- .../powershell/233_iex_dynamic_location.json | 7 +- .../powershell/238_iex_backtick_location.json | 24 ++-- .../245_iex_vertical_tab_location.json | 17 ++- .../254_dynamic_command_location.json | 7 +- .../powershell/257_iex_escape_character.json | 7 +- .../311_v03_substitution_multiple.json | 6 +- .../312_v03_substitution_nested.json | 6 +- .../313_v03_substitution_standalone.json | 2 +- .../314_v03_substitution_call_operator.json | 4 +- ...15_v03_substitution_current_scope_cwd.json | 15 +- .../316_v03_substitution_redirect.json | 4 +- ...03_substitution_expanding_here_string.json | 4 +- ...v03_substitution_dynamic_host_payload.json | 4 +- ..._v03_substitution_static_host_payload.json | 4 +- ..._substitution_direct_comment_boundary.json | 6 +- ...3_substitution_digit_command_identity.json | 4 +- ...03_substitution_dynamic_cwd_poisoning.json | 2 +- ...substitution_decimal_command_identity.json | 4 +- ...03_substitution_dash_command_identity.json | 4 +- .../342_v03_foreach_literal_array.json | 8 +- .../343_v03_foreach_pipeline_iterator.json | 2 +- ...44_v03_foreach_subexpression_iterator.json | 10 +- .../347_v03_foreach_call_operator_alias.json | 2 +- ...3_foreach_numeric_call_operator_alias.json | 2 +- .../350_v03_foreach_semicolon_boundary.json | 18 ++- .../351_v03_foreach_child_host_isolation.json | 4 +- ...v03_foreach_following_and_or_pipeline.json | 18 ++- .../370_v03_foreach_empty_preserves_cwd.json | 4 +- ...e_command_remote_single_target_region.json | 2 +- ..._v03_invoke_command_remote_job_region.json | 2 +- ...ke_command_remote_disconnected_region.json | 2 +- ..._invoke_command_remote_state_isolated.json | 6 +- ...ke_command_remote_nested_comma_scalar.json | 2 +- ...e_command_remote_escaped_comma_scalar.json | 2 +- .../394_v03_redirect_multiple_explicit.json | 2 +- .../395_v03_redirect_dynamic_target.json | 2 +- .../396_v03_redirect_merge_all_streams.json | 2 +- ..._v03_redirect_merge_trailing_argument.json | 2 +- ...03_redirect_all_and_error_independent.json | 2 +- .../409_v03_redirect_wrapper_joined_cwd.json | 4 +- ...3_redirect_encoded_wrapper_joined_cwd.json | 4 +- ..._redirect_unreachable_wrapper_binding.json | 4 +- ..._redirect_unreachable_relative_target.json | 2 +- ..._redirect_unreachable_absolute_target.json | 4 +- ...4_v03_redirect_wrapper_parent_binding.json | 2 +- ...direct_encoded_wrapper_parent_binding.json | 2 +- ...edirect_nested_wrapper_parent_binding.json | 2 +- ...nested_encoded_wrapper_parent_binding.json | 2 +- ...emote_redirect_automatic_home_unknown.json | 2 +- ..._remote_redirect_quoted_tilde_unknown.json | 2 +- ...rect_tilde_after_environment_mutation.json | 8 +- ...encoded_remote_redirect_tilde_unknown.json | 10 +- ...edirect_after_automatic_home_mutation.json | 10 +- ...ded_wrapper_command_binding_isolation.json | 2 +- ...3_encoded_wrapper_clean_home_argument.json | 2 +- ...d_wrapper_resets_parent_home_variable.json | 2 +- ...3_encoded_wrapper_inner_home_mutation.json | 10 +- ...d_encoded_intermediate_redirect_owner.json | 17 ++- ...4_v03_encoded_explicit_native_binding.json | 10 +- ...5_v03_encoded_explicit_script_binding.json | 10 +- ...coded_unqualified_ps1_binding_unknown.json | 2 +- ...79_v03_unknown_initial_automatic_home.json | 2 +- ...known_initial_userprofile_environment.json | 2 +- ...d_wrapper_userprofile_profile_unknown.json | 2 +- ...eceiver_preserves_compatibility_state.json | 24 ++-- ...eceiver_preserves_compatibility_state.json | 30 ++-- .../PwshExecutionRegionBindingCatalogTests.cs | 5 +- .../PwshExecutionRegionStructuralTests.cs | 44 ++---- .../Parsing/PwshForEachStructuralTests.cs | 74 +++++++--- .../Parsing/PwshStructuralProjectionTests.cs | 96 ++++++++----- tools/PwshCorpusTool/CorpusManifest.cs | 27 ++-- 106 files changed, 558 insertions(+), 485 deletions(-) diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md index 653a7aa..3e20c38 100644 --- a/IMPLEMENTATION_PLAN.md +++ b/IMPLEMENTATION_PLAN.md @@ -21,10 +21,11 @@ priorities. approval completeness consistently: prove every authored executable region, but do not require proof of ambient aliases, functions, modules, profiles, executable lookup, or inherited environment state. Preserve the - existing `PwshInitialStateMode` API. Next update default-mode PowerShell - occurrence completeness while keeping loop-dependent effective values - Unknown unless fresh-process state is proved, expand the executable - corpus, and prove the Netclaw approval matrix. Explicit + existing `PwshInitialStateMode` API. Default-mode PowerShell occurrence + completeness now follows that authored boundary while loop-dependent + effective values remain Unknown unless fresh-process state is proved. + Next expand the executable corpus and prove the Netclaw approval matrix. + Explicit source mutation, computed identity, hidden execution, unknown receiver semantics, unsupported constructs, and policy-sensitive unknown values, paths, cwd, or redirects remain strict. @@ -297,8 +298,8 @@ priorities. local state; and invalidate home, cwd, environment, and command-binding facts after uninspected `.ps1` execution. Decoded wrappers rebuild value provenance from preserved inner raw spelling, reset child-process state, - retain explicit native/script binding candidates only when the alpha.3 - constrained command-resolution state proves them unshadowed, clear + retain explicit native/script binding semantics from authored path + spellings without inspecting ambient child state, clear profile-mutable automatic HOME and environment facts, and carry a bounded invocation-owner depth for current, intermediate, and root-owned redirects. Parser-owned binding provenance now distinguishes path-shaped @@ -310,7 +311,7 @@ priorities. pipelines fail atomically. Alpha.3 authorization completeness requires the explicit constrained command-resolution baseline, including after decoded-host boundaries. The v0.3 authored-command approval correction - above supersedes that behavior and is pending implementation. + above supersedes that behavior and is implemented in this slice. Execution-region and loop/state design promotions remain, so OpenSpec task 1.10 stays open. - [x] Deliver the first Bash `$()` substitution slice for supported @@ -416,11 +417,10 @@ priorities. projects iterator and loop-body ancestry, survives decoded wrappers, and fails closed on dynamic iterables, iterator/body state or command-resolution mutation, malformed boundaries, and depth overflow. - Alpha.3 leaves default-mode loop-body and current-scope post-loop - occurrences incomplete; the authored-command correction will make static - occurrences complete without weakening explicit mutation or dynamic - execution checks. Isolated child-host loops do not taint their outer - continuation. + The authored-command correction makes default-mode loop-body and + current-scope post-loop static occurrences complete without weakening + explicit mutation or dynamic execution checks. Isolated child-host loops + do not taint their outer continuation. Alpha.3 added an explicit PowerShell initial-runspace contract and wrapper-state metadata. The additive `PwshInitialStateMode` API remains locked. The v0.3 authored-command correction no longer requires isolated diff --git a/openspec/changes/v0-3-structured-shell-analysis/specs/bounded-shell-analysis/spec.md b/openspec/changes/v0-3-structured-shell-analysis/specs/bounded-shell-analysis/spec.md index b0f86c1..5ab129e 100644 --- a/openspec/changes/v0-3-structured-shell-analysis/specs/bounded-shell-analysis/spec.md +++ b/openspec/changes/v0-3-structured-shell-analysis/specs/bounded-shell-analysis/spec.md @@ -398,9 +398,12 @@ binding facts. #### Scenario: Unknown ambient PowerShell state keeps effective values unknown - **WHEN** default-mode PowerShell parses `foreach ($f in @('a','b')) { Remove-Item -LiteralPath $f }` -- **THEN** the loop structure and body command remain visible and complete +- **THEN** the loop structure and body command remain visible - **THEN** the body occurrence's effective `$f` value is Unknown - **THEN** the parser does not mistake authored iterable text for a runtime value when an ambient binding can coerce or reject it +- **THEN** the occurrence remains incomplete because the unknown provider target can mutate command resolution before a later visit +- **WHEN** the same loop body uses a non-state-mutating receiver such as `Write-Output $f` +- **THEN** its static authored occurrence remains complete while its effective `$f` value remains Unknown #### Scenario: Isolated no-profile runspace permits an ordinary binding proof - **WHEN** the caller selects `IsolatedNonInteractiveNoProfile` for a newly spawned noninteractive no-profile host and parses `foreach ($f in @('a','b')) { Write-Output $f }` @@ -629,7 +632,7 @@ partition merely to publish exact continuation facts. #### Scenario: Unreachable relative redirect has no parse-time cwd proof - **WHEN** isolated-mode PowerShell parses `foreach ($x in @()) { Write-Output x > relative.txt }` -- **THEN** the body occurrence and its working directory remain incomplete or Unknown +- **THEN** the static authored body occurrence remains complete while its working directory is Unknown - **THEN** the explicit redirect target is Unknown rather than the parse-time absolute path - **THEN** an authored absolute redirect target may remain exact because it is cwd-independent diff --git a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshCommandParser.cs b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshCommandParser.cs index 7dfadb5..13dfc1b 100644 --- a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshCommandParser.cs +++ b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshCommandParser.cs @@ -651,6 +651,13 @@ private static ArgumentBindingSemantics ClassifyArgumentBindingSemantics( return new ArgumentBindingSemantics(usesNative); } + // A suffix alone does not prove script binding: an unqualified name + // can resolve to an authored alias or function with the same spelling. + if (command.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase)) + { + return ArgumentBindingSemantics.Unknown; + } + // An unqualified hyphenated name can resolve to either a PowerShell // command or a native executable. Preserve that ambiguity instead of // reconstructing command kind from its spelling during value analysis. diff --git a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshForEachValueAnalysis.cs b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshForEachValueAnalysis.cs index ba75c3c..751cee0 100644 --- a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshForEachValueAnalysis.cs +++ b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshForEachValueAnalysis.cs @@ -1964,9 +1964,6 @@ internal static bool TryAnalyze( initialWorkingDirectory, canPromote: options.InitialStateMode == PwshInitialStateMode.IsolatedNonInteractiveNoProfile, - hasConstrainedCommandResolutionBaseline: - options.InitialStateMode == - PwshInitialStateMode.IsolatedNonInteractiveNoProfile, commandResolutionInvalidated: false, commandResolutionInvalidatedBeyondTrackedMutations: false, allRunspaceCommandResolutionMayReachProcessMutation: false, @@ -2053,19 +2050,12 @@ private PwshFlowResult AnalyzeSimple(SimpleCommandSyntax simple, AnalysisContext includeUnresolved: isForEachIncomplete || current.CommandResolutionInvalidated); var redirects = AnalyzeRedirects(source, current); - var mayPromote = current.CanPromote && - source.HasCompleteValueProvenance && - simple.Substitutions.Count == 0 && - !simple.Clause.IsCommandStringWrapped && - current.CanResolveEveryExpansion(source.ValueProvenance); RecordFacts( simple, current, source, effective, - redirects, - isForEachIncomplete, - mayPromote); + redirects); var hasCommandResolutionMutation = PwshPersistentStateMutation.TryGetCommandResolutionMutation( @@ -2074,7 +2064,7 @@ private PwshFlowResult AnalyzeSimple(SimpleCommandSyntax simple, AnalysisContext providerLocationUnknown: current.WorkingDirectory is null, out var invalidatesAllCommandNames, out var mutatedCommandNames); - var commandIdentityMayMutateState = simple.Clause.Verb.IsDynamic || + var commandIdentityMayMutateState = !IsCommandIdentityProven(simple.Clause, current); var mayMutateAutomaticHome = commandIdentityMayMutateState || @@ -2087,9 +2077,9 @@ private PwshFlowResult AnalyzeSimple(SimpleCommandSyntax simple, AnalysisContext PwshPersistentStateMutation.PreservesCommandResolution(simple.Clause); if (commandIdentityMayMutateState) { - // An unproved invocation can resolve to arbitrary in-process code - // regardless of its authored verb, including through ambient - // aliases, functions, modules, or observed mutations. + // Computed command identities and explicit source mutations can + // hide arbitrary in-process code. Ambient runtime resolution is + // outside the authored-command approval boundary. _nonRegionStateMutationCount++; _childScopeEscapeRiskCount++; _childRunspaceProcessEscapeRiskCount++; @@ -2814,7 +2804,7 @@ private static bool IsCommandIdentityProven( Clause clause, AnalysisContext input) { - if (!input.HasConstrainedCommandResolutionBaseline) + if (clause.Verb.IsDynamic) { return false; } @@ -2830,10 +2820,7 @@ private static bool IsCommandIdentityProven( private static bool IsExecutionRegionReceiverIdentityProven( Clause clause, - AnalysisContext input) => - input.HasConstrainedCommandResolutionBaseline && - !input.CommandResolutionInvalidatedBeyondTrackedMutations && - !input.MayResolveCommandToProcessMutation(clause); + AnalysisContext input) => IsCommandIdentityProven(clause, input); private void RecordExecutionRegions( Clause clause, @@ -2996,9 +2983,7 @@ private void RecordFacts( AnalysisContext input, CommandOccurrenceFacts source, IReadOnlyList effective, - IReadOnlyList redirects, - bool isForEachIncomplete, - bool mayPromote) + IReadOnlyList redirects) { var current = new CommandOccurrenceFacts { @@ -3010,8 +2995,7 @@ private void RecordFacts( ValueProvenance = source.ValueProvenance, HasCompleteValueProvenance = source.HasCompleteValueProvenance, IsComplete = source.IsComplete && - IsCommandIdentityProven(simple.Clause, input) && - (!isForEachIncomplete || mayPromote), + IsCommandIdentityProven(simple.Clause, input), }; if (!_facts.TryGetValue(simple.Clause, out var prior)) { @@ -3612,7 +3596,10 @@ private PwshFlowResult AnalyzeForEach(ForEachSyntax forEach, AnalysisContext inp if (plan.Cardinality == PwshIterationCardinality.Never) { - RecordUnvisitedBindingArguments(forEach.Body, plan.BindingName); + RecordUnvisitedBindingArguments( + forEach.Body, + plan.BindingName, + loopInput); return PwshFlowResult.Success(loopInput); } @@ -3896,7 +3883,7 @@ private CommandOccurrenceFacts GetFacts(SimpleCommandSyntax simple) CwdPathDependencies = source.CwdPathDependencies, ValueProvenance = source.ValueProvenance, HasCompleteValueProvenance = source.HasCompleteValueProvenance, - IsComplete = false, + IsComplete = source.IsComplete, } : source; } @@ -4605,17 +4592,19 @@ private IReadOnlyList RewriteBranches( private void RecordUnvisitedBindingArguments( ShellBlockSyntax block, - string bindingName) + string bindingName, + AnalysisContext input) { foreach (var statement in block.Statements) { - RecordUnvisitedBindingArguments(statement, bindingName); + RecordUnvisitedBindingArguments(statement, bindingName, input); } } private void RecordUnvisitedBindingArguments( ShellSyntaxNode node, - string bindingName) + string bindingName, + AnalysisContext input) { switch (node) { @@ -4643,40 +4632,47 @@ private void RecordUnvisitedBindingArguments( CwdPathDependencies = source.CwdPathDependencies, ValueProvenance = source.ValueProvenance, HasCompleteValueProvenance = source.HasCompleteValueProvenance, - IsComplete = false, + IsComplete = source.IsComplete && + IsCommandIdentityProven(simple.Clause, input), }; foreach (var substitution in simple.Substitutions) { - RecordUnvisitedBindingArguments(substitution.Body, bindingName); + RecordUnvisitedBindingArguments( + substitution.Body, + bindingName, + input); } break; case ShellBlockSyntax nestedBlock: - RecordUnvisitedBindingArguments(nestedBlock, bindingName); + RecordUnvisitedBindingArguments(nestedBlock, bindingName, input); break; case PipelineSyntax pipeline: foreach (var stage in pipeline.Stages) { - RecordUnvisitedBindingArguments(stage, bindingName); + RecordUnvisitedBindingArguments(stage, bindingName, input); } break; case CommandListSyntax list: foreach (var item in list.Items) { - RecordUnvisitedBindingArguments(item.Command, bindingName); + RecordUnvisitedBindingArguments(item.Command, bindingName, input); } break; case GroupSyntax group: - RecordUnvisitedBindingArguments(group.Body, bindingName); + RecordUnvisitedBindingArguments(group.Body, bindingName, input); break; case ForEachSyntax forEach: - RecordUnvisitedBindingArguments(forEach.IteratorCommands, bindingName); - RecordUnvisitedBindingArguments(forEach.Body, bindingName); + RecordUnvisitedBindingArguments( + forEach.IteratorCommands, + bindingName, + input); + RecordUnvisitedBindingArguments(forEach.Body, bindingName, input); break; case CommandSubstitutionSyntax substitution: - RecordUnvisitedBindingArguments(substitution.Body, bindingName); + RecordUnvisitedBindingArguments(substitution.Body, bindingName, input); break; } } @@ -5203,8 +5199,7 @@ private bool TryAnalyzeParserKnownValue( return null; } - return context.HasConstrainedCommandResolutionBaseline && - IsCommandIdentityProven(clause, context) + return IsCommandIdentityProven(clause, context) ? provenance.UsesNativeArgumentBinding : null; } @@ -5277,7 +5272,6 @@ private readonly struct AnalysisContext internal AnalysisContext( string? workingDirectory, bool canPromote, - bool hasConstrainedCommandResolutionBaseline, bool commandResolutionInvalidated, bool commandResolutionInvalidatedBeyondTrackedMutations, bool allRunspaceCommandResolutionMayReachProcessMutation, @@ -5290,8 +5284,6 @@ internal AnalysisContext( { WorkingDirectory = workingDirectory; CanPromote = canPromote; - HasConstrainedCommandResolutionBaseline = - hasConstrainedCommandResolutionBaseline; CommandResolutionInvalidated = commandResolutionInvalidated; CommandResolutionInvalidatedBeyondTrackedMutations = commandResolutionInvalidatedBeyondTrackedMutations; @@ -5310,8 +5302,6 @@ internal AnalysisContext( internal bool CanPromote { get; } - internal bool HasConstrainedCommandResolutionBaseline { get; } - internal bool CommandResolutionInvalidated { get; } internal bool CommandResolutionInvalidatedBeyondTrackedMutations { get; } @@ -5373,7 +5363,6 @@ internal AnalysisContext Invalidate( return new AnalysisContext( unknownCwd ? null : WorkingDirectory, false, - HasConstrainedCommandResolutionBaseline, commandResolutionInvalidated, CommandResolutionInvalidatedBeyondTrackedMutations || invalidateCommandResolution, @@ -5397,7 +5386,6 @@ internal AnalysisContext Invalidate( return new AnalysisContext( unknownCwd ? null : WorkingDirectory, false, - HasConstrainedCommandResolutionBaseline, commandResolutionInvalidated, CommandResolutionInvalidatedBeyondTrackedMutations || invalidateCommandResolution, @@ -5414,7 +5402,6 @@ internal AnalysisContext WithRunspaceCommandResolutionProcessRisk() => new( WorkingDirectory, CanPromote, - HasConstrainedCommandResolutionBaseline, commandResolutionInvalidated: true, commandResolutionInvalidatedBeyondTrackedMutations: true, allRunspaceCommandResolutionMayReachProcessMutation: true, @@ -5449,7 +5436,6 @@ internal AnalysisContext WithRunspaceCommandResolutionProcessRisk( : new AnalysisContext( WorkingDirectory, CanPromote, - HasConstrainedCommandResolutionBaseline, commandResolutionInvalidated: true, CommandResolutionInvalidatedBeyondTrackedMutations, allRunspaceCommandResolutionMayReachProcessMutation: false, @@ -5473,7 +5459,6 @@ internal AnalysisContext WithProcessWideStateInvalidated() => new( WorkingDirectory, CanPromote, - HasConstrainedCommandResolutionBaseline, commandResolutionInvalidated: true, commandResolutionInvalidatedBeyondTrackedMutations: true, AllRunspaceCommandResolutionMayReachProcessMutation, @@ -5493,7 +5478,6 @@ internal AnalysisContext WithoutConfiguredHome() => new( WorkingDirectory, CanPromote, - HasConstrainedCommandResolutionBaseline, CommandResolutionInvalidated, CommandResolutionInvalidatedBeyondTrackedMutations, AllRunspaceCommandResolutionMayReachProcessMutation, @@ -5508,7 +5492,6 @@ internal AnalysisContext WithoutAutomaticHomeValue() => new( WorkingDirectory, CanPromote, - HasConstrainedCommandResolutionBaseline, CommandResolutionInvalidated, CommandResolutionInvalidatedBeyondTrackedMutations, AllRunspaceCommandResolutionMayReachProcessMutation, @@ -5523,7 +5506,6 @@ internal AnalysisContext WithoutBindings() => new( WorkingDirectory, CanPromote, - HasConstrainedCommandResolutionBaseline, CommandResolutionInvalidated, CommandResolutionInvalidatedBeyondTrackedMutations, AllRunspaceCommandResolutionMayReachProcessMutation, @@ -5538,7 +5520,6 @@ internal AnalysisContext CreateChildProcessInput() => new( WorkingDirectory, canPromote: false, - hasConstrainedCommandResolutionBaseline: false, commandResolutionInvalidated: ProcessWideStateInvalidated, commandResolutionInvalidatedBeyondTrackedMutations: ProcessWideStateInvalidated, @@ -5554,7 +5535,6 @@ internal AnalysisContext CreateDecodedHostInput() => new( WorkingDirectory, canPromote: false, - hasConstrainedCommandResolutionBaseline: false, commandResolutionInvalidated: ProcessWideStateInvalidated, commandResolutionInvalidatedBeyondTrackedMutations: ProcessWideStateInvalidated, @@ -5567,15 +5547,14 @@ internal AnalysisContext CreateDecodedHostInput() => Array.Empty()); internal AnalysisContext CreateRemoteInput() => - // The target host or persistent session can have arbitrary cwd, - // variables, aliases, functions, modules, and profiles. Its exit - // state is isolated from the invoking host. + // The target host can have arbitrary cwd and value state. Ambient + // command resolution is outside authored-command completeness, and + // remote exit state is isolated from the invoking host. new( workingDirectory: null, canPromote: false, - hasConstrainedCommandResolutionBaseline: false, commandResolutionInvalidated: true, - commandResolutionInvalidatedBeyondTrackedMutations: true, + commandResolutionInvalidatedBeyondTrackedMutations: false, allRunspaceCommandResolutionMayReachProcessMutation: false, Array.Empty(), processWideStateInvalidated: false, @@ -5590,7 +5569,6 @@ internal AnalysisContext CreateChildRunspaceInput() => new( WorkingDirectory, CanPromote, - HasConstrainedCommandResolutionBaseline, commandResolutionInvalidated: ProcessWideStateInvalidated, commandResolutionInvalidatedBeyondTrackedMutations: ProcessWideStateInvalidated, @@ -5606,7 +5584,6 @@ internal AnalysisContext WithCwd(string? workingDirectory) => new( workingDirectory, CanPromote, - HasConstrainedCommandResolutionBaseline, CommandResolutionInvalidated, CommandResolutionInvalidatedBeyondTrackedMutations, AllRunspaceCommandResolutionMayReachProcessMutation, @@ -5636,7 +5613,6 @@ internal AnalysisContext WithBinding( return new AnalysisContext( WorkingDirectory, CanPromote, - HasConstrainedCommandResolutionBaseline, CommandResolutionInvalidated, CommandResolutionInvalidatedBeyondTrackedMutations, AllRunspaceCommandResolutionMayReachProcessMutation, @@ -5684,32 +5660,6 @@ internal bool TryEvaluateValue( return TryAnalyzeEffectiveValue(value, out domain); } - internal bool CanResolveEveryExpansion( - IReadOnlyList provenance) - { - foreach (var value in provenance) - { - foreach (var fragment in value.Value.Fragments) - { - if (fragment.Kind == ShellValueFragmentKind.Literal) - { - continue; - } - - if (fragment.Kind != ShellValueFragmentKind.Expansion || - fragment.Expansion is not ShellExpansionReference expansion || - expansion.Kind != ShellExpansionKind.Variable || - expansion.Name is null || - FindBinding(expansion.Name) is null) - { - return false; - } - } - } - - return true; - } - internal bool TryAnalyzeEffectiveValue( ShellValue value, out ShellValueDomain domain) @@ -5899,8 +5849,6 @@ internal bool StateEquals(AnalysisContext other) other.WorkingDirectory, StringComparison.Ordinal) || CanPromote != other.CanPromote || - HasConstrainedCommandResolutionBaseline != - other.HasConstrainedCommandResolutionBaseline || CommandResolutionInvalidated != other.CommandResolutionInvalidated || CommandResolutionInvalidatedBeyondTrackedMutations != other.CommandResolutionInvalidatedBeyondTrackedMutations || @@ -5990,8 +5938,6 @@ leftBinding is null || rightBinding is null ? left.WorkingDirectory : null, left.CanPromote && right.CanPromote, - left.HasConstrainedCommandResolutionBaseline && - right.HasConstrainedCommandResolutionBaseline, left.CommandResolutionInvalidated || right.CommandResolutionInvalidated, left.CommandResolutionInvalidatedBeyondTrackedMutations || diff --git a/src/ShellSyntaxTree/PwshParserOptions.cs b/src/ShellSyntaxTree/PwshParserOptions.cs index 346c197..369074b 100644 --- a/src/ShellSyntaxTree/PwshParserOptions.cs +++ b/src/ShellSyntaxTree/PwshParserOptions.cs @@ -5,15 +5,18 @@ // ----------------------------------------------------------------------- namespace ShellSyntaxTree; -/// Declares which ambient PowerShell runspace facts the caller can prove. +/// Compatibility option for PowerShell initial host-state analysis. public enum PwshInitialStateMode { - /// No safe assumption is made about ambient PowerShell runspace state. + /// + /// No safe assumption is made about ambient PowerShell variable-binding + /// state. Static authored command completeness is unaffected. + /// Unknown, /// /// The source runs in a new non-interactive PowerShell process with - /// profiles, startup state, and module discovery constrained as specified. + /// profiles disabled and no reused or caller-initialized runspace. /// IsolatedNonInteractiveNoProfile, } @@ -26,7 +29,7 @@ public sealed record PwshParserOptions : ShellParserOptions { /// /// Gets the caller-proved initial PowerShell runspace-state contract. The - /// default leaves v0.3 command identities and bounded values unproved. + /// default leaves loop-dependent effective values unproved. /// public PwshInitialStateMode InitialStateMode { get; init; } } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/001_simple_get_date.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/001_simple_get_date.json index ebf1f8a..7ad501e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/001_simple_get_date.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/001_simple_get_date.json @@ -41,7 +41,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/063_compound_three_semicolon.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/063_compound_three_semicolon.json index 819a28f..afe50ac 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/063_compound_three_semicolon.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/063_compound_three_semicolon.json @@ -23,13 +23,8 @@ { "raw": "a.txt", "kind": "Literal", - "isPath": true - }, - { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, - "isCwdAttribution": true + "isPath": true, + "resolved": "C:/work/a.txt" } ], "redirects": [] @@ -44,13 +39,8 @@ { "raw": "b.txt", "kind": "Literal", - "isPath": true - }, - { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, - "isCwdAttribution": true + "isPath": true, + "resolved": "C:/work/b.txt" } ], "redirects": [] diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/068_compound_andif_then_semicolon.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/068_compound_andif_then_semicolon.json index 2fc0131..a5ffd54 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/068_compound_andif_then_semicolon.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/068_compound_andif_then_semicolon.json @@ -23,13 +23,8 @@ { "raw": "a.txt", "kind": "Literal", - "isPath": true - }, - { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, - "isCwdAttribution": true + "isPath": true, + "resolved": "C:/work/a.txt" } ], "redirects": [] diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/072_setloc_cd_then_git.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/072_setloc_cd_then_git.json index 78b7d7a..4fec8a5 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/072_setloc_cd_then_git.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/072_setloc_cd_then_git.json @@ -28,9 +28,10 @@ ], "args": [ { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/repo", + "kind": "Literal", + "isPath": true, + "resolved": "C:/repo", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/073_setloc_setlocation_then_gci.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/073_setloc_setlocation_then_gci.json index 5e34abc..0f2e81a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/073_setloc_setlocation_then_gci.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/073_setloc_setlocation_then_gci.json @@ -26,9 +26,10 @@ ], "args": [ { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/src", + "kind": "Literal", + "isPath": true, + "resolved": "C:/src", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/074_setloc_two_cd.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/074_setloc_two_cd.json index 9766c24..324b272 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/074_setloc_two_cd.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/074_setloc_two_cd.json @@ -34,9 +34,10 @@ "resolved": "C:/b" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/a", + "kind": "Literal", + "isPath": true, + "resolved": "C:/a", "isCwdAttribution": true } ], @@ -50,9 +51,10 @@ "canonicalVerb": "Get-ChildItem", "args": [ { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/b", + "kind": "Literal", + "isPath": true, + "resolved": "C:/b", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/077_setloc_path_param.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/077_setloc_path_param.json index ee51247..216ec3a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/077_setloc_path_param.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/077_setloc_path_param.json @@ -34,12 +34,14 @@ { "raw": "temp.txt", "kind": "Literal", - "isPath": true + "isPath": true, + "resolved": "C:/proj/temp.txt" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/proj", + "kind": "Literal", + "isPath": true, + "resolved": "C:/proj", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/078_setloc_through_group.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/078_setloc_through_group.json index 9324bc6..cbfee36 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/078_setloc_through_group.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/078_setloc_through_group.json @@ -33,9 +33,10 @@ "isPath": true }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/sensitive", + "kind": "Literal", + "isPath": true, + "resolved": "C:/sensitive", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/080_setloc_no_arg.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/080_setloc_no_arg.json index c914e3d..059a1c8 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/080_setloc_no_arg.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/080_setloc_no_arg.json @@ -20,9 +20,10 @@ "canonicalVerb": "Get-ChildItem", "args": [ { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/Users/user", + "kind": "Literal", + "isPath": true, + "resolved": "C:/Users/user", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/081_setloc_relative_after_literal.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/081_setloc_relative_after_literal.json index 24d1ec3..e73250a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/081_setloc_relative_after_literal.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/081_setloc_relative_after_literal.json @@ -30,12 +30,14 @@ { "raw": "sub", "kind": "Literal", - "isPath": true + "isPath": true, + "resolved": "C:/repo/sub" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/repo", + "kind": "Literal", + "isPath": true, + "resolved": "C:/repo", "isCwdAttribution": true } ], @@ -49,9 +51,10 @@ "canonicalVerb": "Get-ChildItem", "args": [ { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/repo/sub", + "kind": "Literal", + "isPath": true, + "resolved": "C:/repo/sub", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/084_setloc_andif.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/084_setloc_andif.json index c1d7698..bd76b1f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/084_setloc_andif.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/084_setloc_andif.json @@ -28,9 +28,10 @@ ], "args": [ { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/repo", + "kind": "Literal", + "isPath": true, + "resolved": "C:/repo", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/128_redirect_out.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/128_redirect_out.json index 9ddf9d6..3abcfae 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/128_redirect_out.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/128_redirect_out.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/129_redirect_append.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/129_redirect_append.json index 172d935..3b25845 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/129_redirect_append.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/129_redirect_append.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/130_redirect_err.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/130_redirect_err.json index 38c9249..a834713 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/130_redirect_err.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/130_redirect_err.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/131_redirect_err_append.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/131_redirect_err_append.json index 6401a45..d33e97f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/131_redirect_err_append.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/131_redirect_err_append.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/132_redirect_verbose.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/132_redirect_verbose.json index 0a2f870..2c5b695 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/132_redirect_verbose.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/132_redirect_verbose.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/133_redirect_warning.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/133_redirect_warning.json index 8176315..fed069e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/133_redirect_warning.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/133_redirect_warning.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/134_redirect_debug.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/134_redirect_debug.json index f5c0155..1e85faa 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/134_redirect_debug.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/134_redirect_debug.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/135_redirect_information.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/135_redirect_information.json index ad9d4f6..fdb1c93 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/135_redirect_information.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/135_redirect_information.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/136_redirect_all.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/136_redirect_all.json index fda9e1a..4b0a8c5 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/136_redirect_all.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/136_redirect_all.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/137_redirect_all_append.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/137_redirect_all_append.json index 8be1126..9583d47 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/137_redirect_all_append.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/137_redirect_all_append.json @@ -46,7 +46,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/138_redirect_merge_2to1.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/138_redirect_merge_2to1.json index 206346b..9d385c4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/138_redirect_merge_2to1.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/138_redirect_merge_2to1.json @@ -47,7 +47,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/139_redirect_merge_3to1.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/139_redirect_merge_3to1.json index 55c5cd1..c8110a8 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/139_redirect_merge_3to1.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/139_redirect_merge_3to1.json @@ -47,7 +47,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/143_recursion_command_pipeline.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/143_recursion_command_pipeline.json index cbcb16f..7ccf6de 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/143_recursion_command_pipeline.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/143_recursion_command_pipeline.json @@ -2,9 +2,29 @@ "name": "Recursion command pipeline", "input": "pwsh -Command \u0022gci | rm\u0022", "expected": { - "isUnparseable": true, - "unparseableReasonContains": "PowerShell structural syntax exceeded limits or contained invalid parser-owned facts" + "isUnparseable": false, + "clauses": [ + { + "operator": "None", + "verb": [ + "gci" + ], + "canonicalVerb": "Get-ChildItem", + "args": [], + "redirects": [], + "isCommandStringWrapped": true + }, + { + "operator": "Pipe", + "verb": [ + "rm" + ], + "canonicalVerb": "Remove-Item", + "args": [], + "redirects": [], + "isCommandStringWrapped": true + } + ] }, - "notes": "An unconstrained -Command child pipeline fails atomically because stage identities are not proved.", - "oracleExpectation": "OutOfScope" + "notes": "A static -Command child pipeline retains both authored stages." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/148_recursion_command_setlocation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/148_recursion_command_setlocation.json index 5043188..5624c34 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/148_recursion_command_setlocation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/148_recursion_command_setlocation.json @@ -29,9 +29,10 @@ "canonicalVerb": "Get-ChildItem", "args": [ { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/x", + "kind": "Literal", + "isPath": true, + "resolved": "C:/x", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/152_recursion_encoded_pipeline.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/152_recursion_encoded_pipeline.json index 12d7a81..6837749 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/152_recursion_encoded_pipeline.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/152_recursion_encoded_pipeline.json @@ -2,9 +2,27 @@ "name": "Recursion encoded pipeline", "input": "pwsh -EncodedCommand RwBlAHQALQBQAHIAbwBjAGUAcwBzACAAfAAgAFMAdABvAHAALQBQAHIAbwBjAGUAcwBzAA==", "expected": { - "isUnparseable": true, - "unparseableReasonContains": "PowerShell structural syntax exceeded limits or contained invalid parser-owned facts" + "isUnparseable": false, + "clauses": [ + { + "operator": "None", + "verb": [ + "Get-Process" + ], + "args": [], + "redirects": [], + "isCommandStringWrapped": true + }, + { + "operator": "Pipe", + "verb": [ + "Stop-Process" + ], + "args": [], + "redirects": [], + "isCommandStringWrapped": true + } + ] }, - "notes": "An unconstrained decoded child pipeline fails atomically because stage identities are not proved.", - "oracleExpectation": "OutOfScope" + "notes": "A static decoded child pipeline retains both authored stages." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/162_dynamic_call_scriptblock.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/162_dynamic_call_scriptblock.json index 316a403..54d5797 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/162_dynamic_call_scriptblock.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/162_dynamic_call_scriptblock.json @@ -68,7 +68,7 @@ { "clauseIndex": 0, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/165_dynamic_subexpression_arg.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/165_dynamic_subexpression_arg.json index c1ed9a2..19a1687 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/165_dynamic_subexpression_arg.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/165_dynamic_subexpression_arg.json @@ -88,7 +88,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -116,7 +116,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/226_iex_inherits_location.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/226_iex_inherits_location.json index 2a5e638..8123893 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/226_iex_inherits_location.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/226_iex_inherits_location.json @@ -28,12 +28,14 @@ { "raw": "child.txt", "kind": "Literal", - "isPath": true + "isPath": true, + "resolved": "C:/a/child.txt" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/a", + "kind": "Literal", + "isPath": true, + "resolved": "C:/a", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/227_iex_exports_location.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/227_iex_exports_location.json index e66179b..63ea013 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/227_iex_exports_location.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/227_iex_exports_location.json @@ -29,12 +29,14 @@ { "raw": "child.txt", "kind": "Literal", - "isPath": true + "isPath": true, + "resolved": "C:/b/child.txt" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/b", + "kind": "Literal", + "isPath": true, + "resolved": "C:/b", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/233_iex_dynamic_location.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/233_iex_dynamic_location.json index ae106ad..b6d7b9e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/233_iex_dynamic_location.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/233_iex_dynamic_location.json @@ -32,9 +32,10 @@ "isPath": false }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/safe", + "kind": "Literal", + "isPath": true, + "resolved": "C:/safe", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/238_iex_backtick_location.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/238_iex_backtick_location.json index 9c0bf3d..9505d67 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/238_iex_backtick_location.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/238_iex_backtick_location.json @@ -31,9 +31,10 @@ "isPath": false }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/safe", + "kind": "Literal", + "isPath": true, + "resolved": "C:/safe", "isCwdAttribution": true } ], @@ -53,9 +54,10 @@ "resolved": "C:/evil" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/safe", + "kind": "Literal", + "isPath": true, + "resolved": "C:/safe", "isCwdAttribution": true } ], @@ -71,12 +73,14 @@ { "raw": "child.txt", "kind": "Literal", - "isPath": true + "isPath": true, + "resolved": "C:/evil/child.txt" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/evil", + "kind": "Literal", + "isPath": true, + "resolved": "C:/evil", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/245_iex_vertical_tab_location.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/245_iex_vertical_tab_location.json index 5c4dcad..62c0461 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/245_iex_vertical_tab_location.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/245_iex_vertical_tab_location.json @@ -32,9 +32,10 @@ "resolved": "C:/evil" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/safe", + "kind": "Literal", + "isPath": true, + "resolved": "C:/safe", "isCwdAttribution": true } ], @@ -50,12 +51,14 @@ { "raw": "child.txt", "kind": "Literal", - "isPath": true + "isPath": true, + "resolved": "C:/evil/child.txt" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/evil", + "kind": "Literal", + "isPath": true, + "resolved": "C:/evil", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/254_dynamic_command_location.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/254_dynamic_command_location.json index 438e9b8..ae9dc05 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/254_dynamic_command_location.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/254_dynamic_command_location.json @@ -32,9 +32,10 @@ "isPath": false }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/safe", + "kind": "Literal", + "isPath": true, + "resolved": "C:/safe", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/257_iex_escape_character.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/257_iex_escape_character.json index 1af945c..8da2d54 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/257_iex_escape_character.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/257_iex_escape_character.json @@ -32,9 +32,10 @@ "resolved": "C:/evil" }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/safe", + "kind": "Literal", + "isPath": true, + "resolved": "C:/safe", "isCwdAttribution": true } ], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/311_v03_substitution_multiple.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/311_v03_substitution_multiple.json index 3bf3e00..77c58a0 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/311_v03_substitution_multiple.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/311_v03_substitution_multiple.json @@ -134,7 +134,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -162,7 +162,7 @@ { "clauseIndex": 1, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -190,7 +190,7 @@ { "clauseIndex": 2, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/312_v03_substitution_nested.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/312_v03_substitution_nested.json index b00cb76..f889e61 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/312_v03_substitution_nested.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/312_v03_substitution_nested.json @@ -135,7 +135,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -177,7 +177,7 @@ { "clauseIndex": 1, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -205,7 +205,7 @@ { "clauseIndex": 2, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/313_v03_substitution_standalone.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/313_v03_substitution_standalone.json index 2d765eb..a32d693 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/313_v03_substitution_standalone.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/313_v03_substitution_standalone.json @@ -69,7 +69,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/314_v03_substitution_call_operator.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/314_v03_substitution_call_operator.json index f2f0432..bd769c4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/314_v03_substitution_call_operator.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/314_v03_substitution_call_operator.json @@ -95,7 +95,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -136,5 +136,5 @@ } ] }, - "notes": "The call operator exposes the producer and retains an incomplete dynamic invocation." + "notes": "The call operator exposes the producer while the computed invocation remains incomplete." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/315_v03_substitution_current_scope_cwd.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/315_v03_substitution_current_scope_cwd.json index 432477f..f645d6d 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/315_v03_substitution_current_scope_cwd.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/315_v03_substitution_current_scope_cwd.json @@ -26,9 +26,10 @@ ], "args": [ { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "C:/sensitive", + "kind": "Literal", + "isPath": true, + "resolved": "C:/sensitive", "isCwdAttribution": true } ], @@ -180,7 +181,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -222,7 +223,7 @@ { "clauseIndex": 1, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -264,7 +265,7 @@ { "clauseIndex": 2, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -285,7 +286,7 @@ { "clauseIndex": 3, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/316_v03_substitution_redirect.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/316_v03_substitution_redirect.json index 25e25e1..9411f3a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/316_v03_substitution_redirect.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/316_v03_substitution_redirect.json @@ -99,7 +99,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -127,7 +127,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/317_v03_substitution_expanding_here_string.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/317_v03_substitution_expanding_here_string.json index 4c168ff..c9feba0 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/317_v03_substitution_expanding_here_string.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/317_v03_substitution_expanding_here_string.json @@ -88,7 +88,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -116,7 +116,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/318_v03_substitution_dynamic_host_payload.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/318_v03_substitution_dynamic_host_payload.json index c6bb613..a0a94de 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/318_v03_substitution_dynamic_host_payload.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/318_v03_substitution_dynamic_host_payload.json @@ -93,7 +93,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -134,5 +134,5 @@ } ] }, - "notes": "Parent-scope expansion remains visible beside an incomplete outer pwsh host." + "notes": "Parent-scope expansion remains visible beside the complete decoded authored command." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/319_v03_substitution_static_host_payload.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/319_v03_substitution_static_host_payload.json index 2aa9038..55acd13 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/319_v03_substitution_static_host_payload.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/319_v03_substitution_static_host_payload.json @@ -112,7 +112,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -154,7 +154,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/320_v03_substitution_direct_comment_boundary.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/320_v03_substitution_direct_comment_boundary.json index eb48c4a..4756db4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/320_v03_substitution_direct_comment_boundary.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/320_v03_substitution_direct_comment_boundary.json @@ -124,7 +124,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -159,7 +159,7 @@ { "clauseIndex": 1, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -194,7 +194,7 @@ { "clauseIndex": 2, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/335_v03_substitution_digit_command_identity.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/335_v03_substitution_digit_command_identity.json index 1fc36e3..2eee245 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/335_v03_substitution_digit_command_identity.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/335_v03_substitution_digit_command_identity.json @@ -88,7 +88,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -116,7 +116,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/336_v03_substitution_dynamic_cwd_poisoning.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/336_v03_substitution_dynamic_cwd_poisoning.json index 8f80c57..fd6a6a2 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/336_v03_substitution_dynamic_cwd_poisoning.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/336_v03_substitution_dynamic_cwd_poisoning.json @@ -184,7 +184,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/339_v03_substitution_decimal_command_identity.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/339_v03_substitution_decimal_command_identity.json index b0f0eee..92eef90 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/339_v03_substitution_decimal_command_identity.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/339_v03_substitution_decimal_command_identity.json @@ -88,7 +88,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -116,7 +116,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/341_v03_substitution_dash_command_identity.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/341_v03_substitution_dash_command_identity.json index f2be6a6..ac2cc8b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/341_v03_substitution_dash_command_identity.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/341_v03_substitution_dash_command_identity.json @@ -88,7 +88,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -116,7 +116,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/342_v03_foreach_literal_array.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/342_v03_foreach_literal_array.json index a62bbea..777e16a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/342_v03_foreach_literal_array.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/342_v03_foreach_literal_array.json @@ -128,13 +128,15 @@ } ], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null } } ] }, - "notes": "A static array exposes the loop body while binding-dependent facts remain incomplete." + "notes": "A static array exposes the loop body while policy-sensitive binding facts remain strict." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/343_v03_foreach_pipeline_iterator.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/343_v03_foreach_pipeline_iterator.json index c3d44bf..4b0368a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/343_v03_foreach_pipeline_iterator.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/343_v03_foreach_pipeline_iterator.json @@ -118,7 +118,7 @@ { "clauseIndex": 0, "immediateRole": "Iterator", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/344_v03_foreach_subexpression_iterator.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/344_v03_foreach_subexpression_iterator.json index fa19f91..3e16b48 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/344_v03_foreach_subexpression_iterator.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/344_v03_foreach_subexpression_iterator.json @@ -135,7 +135,7 @@ { "clauseIndex": 0, "immediateRole": "Substitution", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -186,7 +186,7 @@ { "clauseIndex": 1, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -222,8 +222,10 @@ } ], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/347_v03_foreach_call_operator_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/347_v03_foreach_call_operator_alias.json index a142166..d12c260 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/347_v03_foreach_call_operator_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/347_v03_foreach_call_operator_alias.json @@ -48,7 +48,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/349_v03_foreach_numeric_call_operator_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/349_v03_foreach_numeric_call_operator_alias.json index 7598101..8d24461 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/349_v03_foreach_numeric_call_operator_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/349_v03_foreach_numeric_call_operator_alias.json @@ -48,7 +48,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/350_v03_foreach_semicolon_boundary.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/350_v03_foreach_semicolon_boundary.json index fdb5188..aa78928 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/350_v03_foreach_semicolon_boundary.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/350_v03_foreach_semicolon_boundary.json @@ -136,7 +136,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -166,7 +166,7 @@ { "clauseIndex": 1, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -209,8 +209,10 @@ } ], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null } @@ -218,7 +220,7 @@ { "clauseIndex": 2, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -237,8 +239,10 @@ ], "effectiveArguments": [], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/351_v03_foreach_child_host_isolation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/351_v03_foreach_child_host_isolation.json index 28f44ab..31cdab7 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/351_v03_foreach_child_host_isolation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/351_v03_foreach_child_host_isolation.json @@ -140,7 +140,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -208,7 +208,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/352_v03_foreach_following_and_or_pipeline.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/352_v03_foreach_following_and_or_pipeline.json index 7e8eb18..cca6f24 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/352_v03_foreach_following_and_or_pipeline.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/352_v03_foreach_following_and_or_pipeline.json @@ -136,7 +136,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -190,7 +190,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -209,8 +209,10 @@ ], "effectiveArguments": [], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null } @@ -218,7 +220,7 @@ { "clauseIndex": 2, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -237,8 +239,10 @@ ], "effectiveArguments": [], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/370_v03_foreach_empty_preserves_cwd.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/370_v03_foreach_empty_preserves_cwd.json index e237dc1..09cec0b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/370_v03_foreach_empty_preserves_cwd.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/370_v03_foreach_empty_preserves_cwd.json @@ -119,7 +119,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -190,5 +190,5 @@ } ] }, - "notes": "The unreachable body stays visible and incomplete, performs no state transition, and cannot leak parser-time cwd attribution into the continuation." + "notes": "The unreachable authored body stays visible and complete, performs no state transition, and cannot leak parser-time cwd attribution into the continuation." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/384_v03_invoke_command_remote_single_target_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/384_v03_invoke_command_remote_single_target_region.json index c29775c..a1c50ce 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/384_v03_invoke_command_remote_single_target_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/384_v03_invoke_command_remote_single_target_region.json @@ -294,7 +294,7 @@ { "clauseIndex": 1, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/385_v03_invoke_command_remote_job_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/385_v03_invoke_command_remote_job_region.json index a3ed994..4278118 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/385_v03_invoke_command_remote_job_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/385_v03_invoke_command_remote_job_region.json @@ -310,7 +310,7 @@ { "clauseIndex": 1, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/386_v03_invoke_command_remote_disconnected_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/386_v03_invoke_command_remote_disconnected_region.json index 3f75c3f..1e1ae4b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/386_v03_invoke_command_remote_disconnected_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/386_v03_invoke_command_remote_disconnected_region.json @@ -310,7 +310,7 @@ { "clauseIndex": 1, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/390_v03_invoke_command_remote_state_isolated.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/390_v03_invoke_command_remote_state_isolated.json index 5730cd3..92e7f82 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/390_v03_invoke_command_remote_state_isolated.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/390_v03_invoke_command_remote_state_isolated.json @@ -573,7 +573,7 @@ { "clauseIndex": 1, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -622,7 +622,7 @@ { "clauseIndex": 2, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -671,7 +671,7 @@ { "clauseIndex": 3, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/391_v03_invoke_command_remote_nested_comma_scalar.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/391_v03_invoke_command_remote_nested_comma_scalar.json index 9d053ba..46e829a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/391_v03_invoke_command_remote_nested_comma_scalar.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/391_v03_invoke_command_remote_nested_comma_scalar.json @@ -294,7 +294,7 @@ { "clauseIndex": 1, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/392_v03_invoke_command_remote_escaped_comma_scalar.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/392_v03_invoke_command_remote_escaped_comma_scalar.json index 94a90f6..b9461d9 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/392_v03_invoke_command_remote_escaped_comma_scalar.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/392_v03_invoke_command_remote_escaped_comma_scalar.json @@ -294,7 +294,7 @@ { "clauseIndex": 1, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/394_v03_redirect_multiple_explicit.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/394_v03_redirect_multiple_explicit.json index 91f7048..94b0c73 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/394_v03_redirect_multiple_explicit.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/394_v03_redirect_multiple_explicit.json @@ -50,7 +50,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/395_v03_redirect_dynamic_target.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/395_v03_redirect_dynamic_target.json index f2abf55..96e8699 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/395_v03_redirect_dynamic_target.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/395_v03_redirect_dynamic_target.json @@ -47,7 +47,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/396_v03_redirect_merge_all_streams.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/396_v03_redirect_merge_all_streams.json index 0925982..0781825 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/396_v03_redirect_merge_all_streams.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/396_v03_redirect_merge_all_streams.json @@ -47,7 +47,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/397_v03_redirect_merge_trailing_argument.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/397_v03_redirect_merge_trailing_argument.json index d219c7c..b0c24e3 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/397_v03_redirect_merge_trailing_argument.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/397_v03_redirect_merge_trailing_argument.json @@ -53,7 +53,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/404_v03_redirect_all_and_error_independent.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/404_v03_redirect_all_and_error_independent.json index 778704d..2690153 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/404_v03_redirect_all_and_error_independent.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/404_v03_redirect_all_and_error_independent.json @@ -50,7 +50,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/409_v03_redirect_wrapper_joined_cwd.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/409_v03_redirect_wrapper_joined_cwd.json index 9bdfa27..f94f6df 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/409_v03_redirect_wrapper_joined_cwd.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/409_v03_redirect_wrapper_joined_cwd.json @@ -114,7 +114,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -144,7 +144,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/410_v03_redirect_encoded_wrapper_joined_cwd.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/410_v03_redirect_encoded_wrapper_joined_cwd.json index b1bf252..d2c280c 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/410_v03_redirect_encoded_wrapper_joined_cwd.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/410_v03_redirect_encoded_wrapper_joined_cwd.json @@ -114,7 +114,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -144,7 +144,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/411_v03_redirect_unreachable_wrapper_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/411_v03_redirect_unreachable_wrapper_binding.json index c20dd97..3bc7b4e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/411_v03_redirect_unreachable_wrapper_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/411_v03_redirect_unreachable_wrapper_binding.json @@ -111,7 +111,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -176,5 +176,5 @@ } ] }, - "notes": "An unreachable wrapper redirect stays visible, Unknown, and incomplete rather than borrowing a loop value." + "notes": "An unreachable wrapper command stays complete while its redirect remains Unknown rather than borrowing a loop value." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/412_v03_redirect_unreachable_relative_target.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/412_v03_redirect_unreachable_relative_target.json index e861c18..27919ca 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/412_v03_redirect_unreachable_relative_target.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/412_v03_redirect_unreachable_relative_target.json @@ -100,7 +100,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/413_v03_redirect_unreachable_absolute_target.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/413_v03_redirect_unreachable_absolute_target.json index b34aeac..b958226 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/413_v03_redirect_unreachable_absolute_target.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/413_v03_redirect_unreachable_absolute_target.json @@ -93,7 +93,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -146,5 +146,5 @@ } ] }, - "notes": "A cwd-independent absolute redirect target remains exact even on an unreachable incomplete occurrence." + "notes": "A cwd-independent absolute redirect target remains exact on an unreachable complete authored occurrence." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/414_v03_redirect_wrapper_parent_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/414_v03_redirect_wrapper_parent_binding.json index fe2c549..2af3856 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/414_v03_redirect_wrapper_parent_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/414_v03_redirect_wrapper_parent_binding.json @@ -111,7 +111,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/415_v03_redirect_encoded_wrapper_parent_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/415_v03_redirect_encoded_wrapper_parent_binding.json index 39ca43b..fa35be9 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/415_v03_redirect_encoded_wrapper_parent_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/415_v03_redirect_encoded_wrapper_parent_binding.json @@ -111,7 +111,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/416_v03_redirect_nested_wrapper_parent_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/416_v03_redirect_nested_wrapper_parent_binding.json index 8aadfab..9419900 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/416_v03_redirect_nested_wrapper_parent_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/416_v03_redirect_nested_wrapper_parent_binding.json @@ -133,7 +133,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/417_v03_redirect_nested_encoded_wrapper_parent_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/417_v03_redirect_nested_encoded_wrapper_parent_binding.json index 3a04cd2..35cc4f1 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/417_v03_redirect_nested_encoded_wrapper_parent_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/417_v03_redirect_nested_encoded_wrapper_parent_binding.json @@ -133,7 +133,7 @@ { "clauseIndex": 0, "immediateRole": "LoopBody", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/459_v03_remote_redirect_automatic_home_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/459_v03_remote_redirect_automatic_home_unknown.json index 38c4bff..ef880c6 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/459_v03_remote_redirect_automatic_home_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/459_v03_remote_redirect_automatic_home_unknown.json @@ -220,7 +220,7 @@ { "clauseIndex": 1, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/460_v03_remote_redirect_quoted_tilde_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/460_v03_remote_redirect_quoted_tilde_unknown.json index db169e9..5611ed9 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/460_v03_remote_redirect_quoted_tilde_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/460_v03_remote_redirect_quoted_tilde_unknown.json @@ -220,7 +220,7 @@ { "clauseIndex": 1, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/465_v03_encoded_child_redirect_tilde_after_environment_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/465_v03_encoded_child_redirect_tilde_after_environment_mutation.json index b44387b..4675b4d 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/465_v03_encoded_child_redirect_tilde_after_environment_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/465_v03_encoded_child_redirect_tilde_after_environment_mutation.json @@ -266,7 +266,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -343,8 +343,10 @@ ], "effectiveArguments": [], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/466_v03_encoded_remote_redirect_tilde_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/466_v03_encoded_remote_redirect_tilde_unknown.json index 759edae..28e704f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/466_v03_encoded_remote_redirect_tilde_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/466_v03_encoded_remote_redirect_tilde_unknown.json @@ -190,9 +190,9 @@ "listOperator": null, "executionOrigin": "CommandArgument", "hostClauseElementIndex": 3, - "executionPhase": "Unknown", - "executionTiming": "Unknown", - "executionCardinality": "Unknown" + "executionPhase": "Main", + "executionTiming": "Synchronous", + "executionCardinality": "Once" }, { "kind": "Block", @@ -221,7 +221,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -258,7 +258,7 @@ { "clauseIndex": 1, "immediateRole": "ExecutionRegion", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/467_v03_encoded_redirect_after_automatic_home_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/467_v03_encoded_redirect_after_automatic_home_mutation.json index e70797e..a7172ca 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/467_v03_encoded_redirect_after_automatic_home_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/467_v03_encoded_redirect_after_automatic_home_mutation.json @@ -205,7 +205,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -249,7 +249,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -282,8 +282,10 @@ ], "effectiveArguments": [], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/468_v03_encoded_wrapper_command_binding_isolation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/468_v03_encoded_wrapper_command_binding_isolation.json index c86ccbf..51cc0ee 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/468_v03_encoded_wrapper_command_binding_isolation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/468_v03_encoded_wrapper_command_binding_isolation.json @@ -95,7 +95,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/469_v03_encoded_wrapper_clean_home_argument.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/469_v03_encoded_wrapper_clean_home_argument.json index fa12278..ade2115 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/469_v03_encoded_wrapper_clean_home_argument.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/469_v03_encoded_wrapper_clean_home_argument.json @@ -97,7 +97,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/470_v03_encoded_wrapper_resets_parent_home_variable.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/470_v03_encoded_wrapper_resets_parent_home_variable.json index 2516586..137d90e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/470_v03_encoded_wrapper_resets_parent_home_variable.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/470_v03_encoded_wrapper_resets_parent_home_variable.json @@ -219,7 +219,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/472_v03_encoded_wrapper_inner_home_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/472_v03_encoded_wrapper_inner_home_mutation.json index 13e029f..338a70c 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/472_v03_encoded_wrapper_inner_home_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/472_v03_encoded_wrapper_inner_home_mutation.json @@ -190,7 +190,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -234,7 +234,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -277,8 +277,10 @@ } ], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/473_v03_nested_encoded_intermediate_redirect_owner.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/473_v03_nested_encoded_intermediate_redirect_owner.json index e5196ed..6ab7e7e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/473_v03_nested_encoded_intermediate_redirect_owner.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/473_v03_nested_encoded_intermediate_redirect_owner.json @@ -58,9 +58,10 @@ "isPath": false }, { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, + "raw": "/tmp/inner", + "kind": "Literal", + "isPath": true, + "resolved": "/tmp/inner", "isCwdAttribution": true } ], @@ -203,7 +204,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -261,7 +262,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -308,8 +309,10 @@ ], "effectiveArguments": [], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "/tmp/inner" + ], "pattern": null, "coveringDirectory": null }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/474_v03_encoded_explicit_native_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/474_v03_encoded_explicit_native_binding.json index 401f377..4a35460 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/474_v03_encoded_explicit_native_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/474_v03_encoded_explicit_native_binding.json @@ -97,7 +97,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -125,8 +125,10 @@ { "clauseElementIndex": 1, "value": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/Users/user" + ], "pattern": null, "coveringDirectory": null } @@ -143,5 +145,5 @@ } ] }, - "notes": "An explicit executable path supplies a native-binding candidate, but an unconstrained decoded child cannot prove it was not shadowed." + "notes": "An explicit executable path supplies authored native-binding semantics without inspecting ambient child state." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/475_v03_encoded_explicit_script_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/475_v03_encoded_explicit_script_binding.json index a1ddd18..d670fa4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/475_v03_encoded_explicit_script_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/475_v03_encoded_explicit_script_binding.json @@ -97,7 +97,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -125,8 +125,10 @@ { "clauseElementIndex": 1, "value": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "~" + ], "pattern": null, "coveringDirectory": null } @@ -143,5 +145,5 @@ } ] }, - "notes": "An explicit script path supplies a PowerShell-binding candidate, but an unconstrained decoded child cannot prove it was not shadowed." + "notes": "An explicit script path supplies authored PowerShell-binding semantics without inspecting ambient child state." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/477_v03_encoded_unqualified_ps1_binding_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/477_v03_encoded_unqualified_ps1_binding_unknown.json index acb82b2..644a228 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/477_v03_encoded_unqualified_ps1_binding_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/477_v03_encoded_unqualified_ps1_binding_unknown.json @@ -97,7 +97,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/479_v03_unknown_initial_automatic_home.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/479_v03_unknown_initial_automatic_home.json index 70459e5..9433866 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/479_v03_unknown_initial_automatic_home.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/479_v03_unknown_initial_automatic_home.json @@ -73,7 +73,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/481_v03_unknown_initial_userprofile_environment.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/481_v03_unknown_initial_userprofile_environment.json index d39b17d..cd6b7b3 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/481_v03_unknown_initial_userprofile_environment.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/481_v03_unknown_initial_userprofile_environment.json @@ -73,7 +73,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/485_v03_encoded_wrapper_userprofile_profile_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/485_v03_encoded_wrapper_userprofile_profile_unknown.json index d71aad6..072bb59 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/485_v03_encoded_wrapper_userprofile_profile_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/485_v03_encoded_wrapper_userprofile_profile_unknown.json @@ -97,7 +97,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/486_v03_unknown_receiver_preserves_compatibility_state.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/486_v03_unknown_receiver_preserves_compatibility_state.json index 312079e..285d99f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/486_v03_unknown_receiver_preserves_compatibility_state.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/486_v03_unknown_receiver_preserves_compatibility_state.json @@ -34,13 +34,8 @@ { "raw": "relative.txt", "kind": "Literal", - "isPath": true - }, - { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, - "isCwdAttribution": true + "isPath": true, + "resolved": "C:/work/relative.txt" } ], "redirects": [], @@ -65,7 +60,8 @@ "precedingVerbElementCount": 1, "kind": "Literal", "isFlag": false, - "isPath": true + "isPath": true, + "resolved": "C:/work/relative.txt" } ] } @@ -120,7 +116,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -150,7 +146,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -169,13 +165,15 @@ ], "effectiveArguments": [], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null } } ] }, - "notes": "Ambient uncertainty preserves both v0.2 leaves while the first unproved invocation invalidates the following authorization cwd and path facts." + "notes": "Ambient uncertainty preserves both complete authored leaves and does not poison parser-owned cwd or path facts." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/487_v03_encoded_unknown_receiver_preserves_compatibility_state.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/487_v03_encoded_unknown_receiver_preserves_compatibility_state.json index dabe67b..e6557b7 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/487_v03_encoded_unknown_receiver_preserves_compatibility_state.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/487_v03_encoded_unknown_receiver_preserves_compatibility_state.json @@ -37,19 +37,12 @@ "raw": "hi", "kind": "Literal", "isPath": false - }, - { - "raw": "\u003Cdynamic-cwd\u003E", - "kind": "DynamicSkip", - "isPath": false, - "isCwdAttribution": true } ], "redirects": [ { "direction": "Out", - "target": "relative.txt", - "isDynamicSkip": true + "target": "C:/work/relative.txt" } ], "elements": [ @@ -84,7 +77,8 @@ "precedingVerbElementCount": 1, "kind": "Literal", "isFlag": false, - "isPath": true + "isPath": true, + "resolved": "C:/work/relative.txt" } ], "isCommandStringWrapped": true @@ -162,7 +156,7 @@ { "clauseIndex": 0, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -206,7 +200,7 @@ { "clauseIndex": 1, "immediateRole": "Ordinary", - "isComplete": false, + "isComplete": true, "ancestry": [ { "ancestorKind": "Block", @@ -239,8 +233,10 @@ ], "effectiveArguments": [], "workingDirectory": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work" + ], "pattern": null, "coveringDirectory": null }, @@ -252,8 +248,10 @@ "operation": "FileOutput", "targetDescriptor": null, "target": { - "kind": "Unknown", - "values": [], + "kind": "Exact", + "values": [ + "C:/work/relative.txt" + ], "pattern": null, "coveringDirectory": null }, @@ -264,5 +262,5 @@ } ] }, - "notes": "An unconstrained decoded receiver remains visible and incomplete; its first unproved invocation makes the following redirect and cwd facts unknown." + "notes": "A static decoded receiver remains visible and complete without poisoning the following authored redirect or cwd facts." } diff --git a/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionBindingCatalogTests.cs b/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionBindingCatalogTests.cs index 23c3646..b48343f 100644 --- a/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionBindingCatalogTests.cs +++ b/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionBindingCatalogTests.cs @@ -97,7 +97,7 @@ public void Static_command_spellings_share_the_canonical_receiver(string source) } [Fact] - public void Module_qualified_catalog_lookup_is_separate_from_identity_proof() + public void Module_qualified_catalog_lookup_uses_authored_identity() { var resolved = PwshExecutionRegionBindingCatalog.TryResolveStaticCommandName( "Microsoft.PowerShell.Core\\ForEach-Object", @@ -110,11 +110,12 @@ public void Module_qualified_catalog_lookup_is_separate_from_identity_proof() Assert.True(resolved); Assert.Equal("ForEach-Object", canonical); Assert.Equal( - ExecutionRegionPhase.Unknown, + ExecutionRegionPhase.Process, Assert.Single( Assert.IsType( Assert.Single(conservative.Syntax.Statements)) .ExecutionRegions).Phase); + Assert.All(conservative.Commands, command => Assert.True(command.IsComplete)); Assert.False(parsed.IsUnparseable); Assert.Equal(2, parsed.Commands.Count); Assert.Equal(2, parsed.Clauses.Count); diff --git a/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionStructuralTests.cs b/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionStructuralTests.cs index 39f955c..9f4d9ce 100644 --- a/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionStructuralTests.cs +++ b/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionStructuralTests.cs @@ -165,7 +165,7 @@ public void Remote_invoke_command_publishes_proved_region_facts( Assert.Equal(expectedHostComplete, result.Commands[0].IsComplete); var body = result.Commands[1]; Assert.Equal("Get-Date", body.Clause.Verb.Tokens[0]); - Assert.False(body.IsComplete); + Assert.Equal(expectedHostComplete, body.IsComplete); Assert.Equal(ShellValueDomainKind.Unknown, body.WorkingDirectory.Kind); } @@ -206,7 +206,7 @@ public void Remote_invoke_command_does_not_inherit_or_export_bindings() Assert.Equal( ShellValueDomainKind.Unknown, Assert.Single(writes[0].EffectiveArguments).Value.Kind); - Assert.False(writes[0].IsComplete); + Assert.True(writes[0].IsComplete); Assert.Equal( new[] { "outer" }, Assert.Single(writes[1].EffectiveArguments).Value.Values); @@ -590,7 +590,7 @@ public void Start_job_publishes_a_concurrent_once_main_region(string source) Assert.Equal(ExecutionRegionCardinality.Once, region.Cardinality); Assert.Equal(2, result.Commands.Count); Assert.True(result.Commands[0].IsComplete); - Assert.False(result.Commands[1].IsComplete); + Assert.True(result.Commands[1].IsComplete); } [Fact] @@ -628,9 +628,9 @@ public void Start_job_applies_working_directory_before_initialization_and_isolat .ToArray(); Assert.Equal(3, items.Length); Assert.Equal("/tmp", Assert.Single(items[0].WorkingDirectory.Values)); - Assert.Equal(ShellValueDomainKind.Unknown, items[1].WorkingDirectory.Kind); + Assert.Equal("/tmp", Assert.Single(items[1].WorkingDirectory.Values)); Assert.Equal("C:/work", Assert.Single(items[2].WorkingDirectory.Values)); - Assert.All(items.Take(2), command => Assert.False(command.IsComplete)); + Assert.All(items.Take(2), command => Assert.True(command.IsComplete)); Assert.True(items[2].IsComplete); } @@ -651,7 +651,7 @@ public void Start_job_accepts_a_bounded_host_working_directory_value( Assert.Equal( new[] { "/tmp" }, child.WorkingDirectory.Values); - Assert.False(child.IsComplete); + Assert.True(child.IsComplete); } [Theory] @@ -669,7 +669,7 @@ public void Start_job_resolves_static_host_working_directory_forms( Assert.Equal( new[] { expected }, child.WorkingDirectory.Values); - Assert.False(child.IsComplete); + Assert.True(child.IsComplete); } [Theory] @@ -701,7 +701,7 @@ public void Start_job_preserves_significant_inline_working_directory_whitespace( var child = result.Commands.Last(); Assert.Equal(ShellValueDomainKind.Unknown, child.WorkingDirectory.Kind); - Assert.False(child.IsComplete); + Assert.True(child.IsComplete); } [Theory] @@ -780,7 +780,7 @@ public void Start_job_does_not_inherit_host_bindings_or_export_child_bindings() Assert.Equal( ShellValueDomainKind.Unknown, Assert.Single(write.EffectiveArguments).Value.Kind); - Assert.False(write.IsComplete); + Assert.True(write.IsComplete); }); Assert.Equal( new[] { "outer" }, @@ -816,7 +816,7 @@ public void Dynamic_start_job_working_directory_fails_closed_only_in_the_child() .ToArray(); Assert.Equal(2, items.Length); Assert.Equal(ShellValueDomainKind.Unknown, items[0].WorkingDirectory.Kind); - Assert.False(items[0].IsComplete); + Assert.True(items[0].IsComplete); Assert.Equal( new[] { "C:/work" }, items[1].WorkingDirectory.Values); @@ -1109,9 +1109,9 @@ public void Multiple_script_blocks_keep_authored_regions_and_host_coordinates() [InlineData("echo { Remove-Item victim.txt }")] [InlineData( "Microsoft.PowerShell.Utility\\Write-Output { Remove-Item victim.txt }")] - public void Proved_data_receiver_keeps_script_block_opaque(string source) + public void Authored_data_receiver_keeps_script_block_opaque(string source) { - var result = ParseIsolated(source); + var result = Parse(source); var host = Assert.IsType(Assert.Single(result.Syntax.Statements)); Assert.Empty(host.ExecutionRegions); @@ -1121,18 +1121,6 @@ public void Proved_data_receiver_keeps_script_block_opaque(string source) Assert.Single(result.Clauses); } - [Fact] - public void Module_qualified_data_receiver_requires_a_constrained_baseline() - { - var result = Parse( - "Microsoft.PowerShell.Utility\\Write-Output { Remove-Item victim.txt }"); - - var host = Assert.IsType(Assert.Single(result.Syntax.Statements)); - Assert.Single(host.ExecutionRegions); - Assert.Equal(2, result.Commands.Count); - Assert.All(result.Commands, command => Assert.False(command.IsComplete)); - } - [Fact] public void Observed_receiver_mutation_prevents_a_false_data_proof() { @@ -1185,12 +1173,10 @@ public void Canonical_alias_target_mutation_prevents_a_false_data_proof() Assert.All(result.Commands.Skip(1), command => Assert.False(command.IsComplete)); } - [Theory] - [InlineData("Write-Output { Remove-Item victim.txt }")] - [InlineData("Invoke-Custom { Remove-Item victim.txt }")] - public void Unproved_receiver_identity_never_hides_a_script_block(string source) + [Fact] + public void Unknown_receiver_semantics_never_hide_a_script_block() { - var result = Parse(source); + var result = Parse("Invoke-Custom { Remove-Item victim.txt }"); var host = Assert.IsType(Assert.Single(result.Syntax.Statements)); Assert.Single(host.ExecutionRegions); diff --git a/tests/ShellSyntaxTree.Tests/Parsing/PwshForEachStructuralTests.cs b/tests/ShellSyntaxTree.Tests/Parsing/PwshForEachStructuralTests.cs index 345ab3f..04fcf2c 100644 --- a/tests/ShellSyntaxTree.Tests/Parsing/PwshForEachStructuralTests.cs +++ b/tests/ShellSyntaxTree.Tests/Parsing/PwshForEachStructuralTests.cs @@ -98,7 +98,29 @@ public void Direct_subexpression_iterator_discovers_inner_command() Assert.Contains(result.Commands[0].Ancestry, frame => frame.Region == CommandAncestryRegion.Iterator); Assert.Equal(CommandOccurrenceRole.LoopBody, result.Commands[1].ImmediateRole); - Assert.False(result.Commands[1].IsComplete); + Assert.True(result.Commands[1].IsComplete); + } + + [Fact] + public void Default_state_keeps_loop_command_complete_without_promoting_value() + { + var result = Parse( + "foreach ($f in @('a','b')) { Write-Output $f }"); + + var command = Assert.Single(result.Commands); + Assert.True(command.IsComplete); + Assert.Equal( + ShellValueDomainKind.Unknown, + Assert.Single(command.EffectiveArguments).Value.Kind); + } + + [Fact] + public void Default_state_keeps_static_pipeline_stages_complete() + { + var result = Parse("Get-ChildItem | Remove-Item"); + + Assert.Equal(2, result.Commands.Count); + Assert.All(result.Commands, command => Assert.True(command.IsComplete)); } [Fact] @@ -117,7 +139,7 @@ public void Nested_foreach_statements_preserve_distinct_loop_ancestry() occurrence.Ancestry.Count(frame => frame.AncestorKind == ShellSyntaxKind.ForEach && frame.Region == CommandAncestryRegion.LoopBody)); - Assert.False(occurrence.IsComplete); + Assert.True(occurrence.IsComplete); } [Fact] @@ -135,7 +157,7 @@ public void Decoded_host_wrapper_retains_foreach_structure_without_outer_spans() Assert.Null(loop.Binding.Source.SourceLength); Assert.Null(loop.Iterable.SourceStart); Assert.Null(loop.Iterable.SourceLength); - Assert.False(Assert.Single(result.Commands).IsComplete); + Assert.True(Assert.Single(result.Commands).IsComplete); } [Theory] @@ -242,9 +264,7 @@ public void Semicolon_remains_a_valid_foreach_statement_boundary() var list = Assert.IsType(Assert.Single(result.Syntax.Statements)); Assert.Equal(3, list.Items.Count); Assert.IsType(list.Items[1].Command); - Assert.False(result.Commands[0].IsComplete); - Assert.False(result.Commands[1].IsComplete); - Assert.False(result.Commands[2].IsComplete); + Assert.All(result.Commands, command => Assert.True(command.IsComplete)); } [Fact] @@ -258,7 +278,7 @@ public void Separate_and_or_pipeline_after_foreach_statement_remains_valid() Assert.Equal(3, list.Items.Count); Assert.Equal(CompoundOperator.Sequence, list.Items[1].Operator); Assert.Equal(CompoundOperator.AndIf, list.Items[2].Operator); - Assert.All(result.Commands, command => Assert.False(command.IsComplete)); + Assert.All(result.Commands, command => Assert.True(command.IsComplete)); } [Fact] @@ -269,12 +289,12 @@ public void Isolated_child_host_loop_does_not_taint_outer_continuation() Assert.False(result.IsUnparseable, result.UnparseableReason); Assert.Equal(2, result.Commands.Count); - Assert.False(result.Commands[0].IsComplete); + Assert.True(result.Commands[0].IsComplete); Assert.True(result.Commands[1].IsComplete); } [Fact] - public void Decoded_child_host_iterator_stays_visible_but_incomplete() + public void Decoded_child_host_iterator_stays_visible_and_complete() { var result = ParseIsolated( "pwsh -Command 'foreach ($x in Get-Item C:\\input) " + @@ -283,8 +303,8 @@ public void Decoded_child_host_iterator_stays_visible_but_incomplete() Assert.False(result.IsUnparseable, result.UnparseableReason); Assert.Equal(new[] { "Get-Item", "Write-Output", "Get-Date" }, result.Commands.Select(CommandVerb)); - Assert.False(result.Commands[0].IsComplete); - Assert.False(result.Commands[1].IsComplete); + Assert.True(result.Commands[0].IsComplete); + Assert.True(result.Commands[1].IsComplete); Assert.True(result.Commands[2].IsComplete); } @@ -348,7 +368,7 @@ public void Isolated_literal_array_publishes_distinct_finite_binding_values() } [Fact] - public void Default_initial_state_withholds_binding_proof() + public void Default_initial_state_keeps_policy_sensitive_binding_strict() { var result = Parse( "foreach ($f in @('a.txt', 'b.txt')) { Remove-Item -LiteralPath $f }"); @@ -440,9 +460,9 @@ public void Empty_foreach_preserves_cwd_and_does_not_invent_a_binding() Assert.False(result.IsUnparseable, result.UnparseableReason); Assert.Equal(3, result.Commands.Count); - Assert.False(result.Commands[0].IsComplete); + Assert.True(result.Commands[0].IsComplete); Assert.Equal(ShellValueDomainKind.Unknown, result.Commands[0].WorkingDirectory.Kind); - Assert.False(result.Commands[1].IsComplete); + Assert.True(result.Commands[1].IsComplete); Assert.Equal( ShellValueDomainKind.Unknown, Assert.Single(result.Commands[1].EffectiveArguments).Value.Kind); @@ -560,14 +580,14 @@ public void Runtime_iterator_uses_zero_or_more_post_loop_binding_state() } [Fact] - public void Prior_variable_mutation_invalidates_later_binding_proof() + public void Prior_variable_mutation_keeps_static_occurrence_complete() { var result = ParseIsolated( "Set-Variable f seeded; foreach ($f in 'value') { Write-Output $f }"); Assert.False(result.IsUnparseable, result.UnparseableReason); Assert.Equal(2, result.Commands.Count); - Assert.False(result.Commands[1].IsComplete); + Assert.True(result.Commands[1].IsComplete); Assert.Equal( ShellValueDomainKind.Unknown, Assert.Single(result.Commands[1].EffectiveArguments).Value.Kind); @@ -577,6 +597,20 @@ public void Prior_variable_mutation_invalidates_later_binding_proof() "C:/work"); } + [Fact] + public void Default_state_prior_variable_mutation_keeps_static_occurrence_complete() + { + var result = Parse( + "Set-Variable f seeded; foreach ($f in 'value') { Write-Output $f }"); + + Assert.False(result.IsUnparseable, result.UnparseableReason); + Assert.Equal(2, result.Commands.Count); + Assert.True(result.Commands[1].IsComplete); + Assert.Equal( + ShellValueDomainKind.Unknown, + Assert.Single(result.Commands[1].EffectiveArguments).Value.Kind); + } + [Theory] [InlineData("-OutVariable f")] [InlineData("-ov f")] @@ -1106,7 +1140,7 @@ public void Outer_wrapper_redirect_uses_parent_loop_binding_domain() Assert.False(result.IsUnparseable, result.UnparseableReason); var command = Assert.Single(result.Commands); - Assert.False(command.IsComplete); + Assert.True(command.IsComplete); var redirect = Assert.Single(command.Redirects); Assert.Equal(ShellValueDomainKind.FiniteSet, redirect.Target.Kind); Assert.Equal( @@ -1127,7 +1161,7 @@ public void Nested_wrapper_redirect_uses_outermost_parent_loop_binding_domain( Assert.False(result.IsUnparseable, result.UnparseableReason); var command = Assert.Single(result.Commands); - Assert.False(command.IsComplete); + Assert.True(command.IsComplete); var redirect = Assert.Single(command.Redirects); Assert.Equal(ShellValueDomainKind.FiniteSet, redirect.Target.Kind); Assert.Equal( @@ -1144,7 +1178,7 @@ public void Unreachable_relative_redirect_does_not_retain_parse_time_cwd() Assert.False(result.IsUnparseable, result.UnparseableReason); var command = Assert.Single(result.Commands); - Assert.False(command.IsComplete); + Assert.True(command.IsComplete); Assert.Equal(ShellValueDomainKind.Unknown, command.WorkingDirectory.Kind); Assert.True(Assert.Single(command.Clause.Redirects).IsDynamicSkip); var redirect = Assert.Single(command.Redirects); @@ -1160,7 +1194,7 @@ public void Unreachable_absolute_redirect_remains_cwd_independent() Assert.False(result.IsUnparseable, result.UnparseableReason); var command = Assert.Single(result.Commands); - Assert.False(command.IsComplete); + Assert.True(command.IsComplete); var redirect = Assert.Single(command.Redirects); Assert.Equal(ShellValueDomainKind.Exact, redirect.Target.Kind); Assert.Equal("C:/fixed.txt", Assert.Single(redirect.Target.Values)); diff --git a/tests/ShellSyntaxTree.Tests/Parsing/PwshStructuralProjectionTests.cs b/tests/ShellSyntaxTree.Tests/Parsing/PwshStructuralProjectionTests.cs index 7c3c906..ffe6961 100644 --- a/tests/ShellSyntaxTree.Tests/Parsing/PwshStructuralProjectionTests.cs +++ b/tests/ShellSyntaxTree.Tests/Parsing/PwshStructuralProjectionTests.cs @@ -186,7 +186,7 @@ public void Joined_cwd_keeps_outer_wrapper_redirect_target_unknown( var redirect = Assert.Single(command.Redirects); Assert.Equal(ShellValueDomainKind.Unknown, redirect.Target.Kind); Assert.True(redirect.IsComplete); - Assert.False(command.IsComplete); + Assert.True(command.IsComplete); } [Theory] @@ -203,7 +203,7 @@ public void Success_only_cwd_resolves_outer_wrapper_redirect_target( var redirect = Assert.Single(command.Redirects); Assert.Equal(ShellValueDomainKind.Exact, redirect.Target.Kind); Assert.Equal("C:/maybe/relative.txt", Assert.Single(redirect.Target.Values)); - Assert.False(command.IsComplete); + Assert.True(command.IsComplete); } [Theory] @@ -232,9 +232,7 @@ public void Dynamic_provider_failure_promotes_static_paths_and_redirect_facts( var redirect = Assert.Single(clause.Redirects); Assert.False(redirect.IsDynamicSkip); Assert.Equal("C:/work/out.txt", redirect.Target); - Assert.Equal( - !invocation.StartsWith("pwsh", StringComparison.Ordinal), - command.IsComplete); + Assert.True(command.IsComplete); var redirectFact = Assert.Single(command.Redirects); Assert.Equal(RedirectOperation.FileOutput, redirectFact.Operation); Assert.Equal("C:/work/out.txt", Assert.Single(redirectFact.Target.Values)); @@ -292,9 +290,7 @@ public void Dynamic_provider_failure_promotes_a_quoted_comma_filename( Assert.False(result.IsUnparseable, result.UnparseableReason); var command = result.Commands[1]; - Assert.Equal( - !invocation.StartsWith("pwsh", StringComparison.Ordinal), - command.IsComplete); + Assert.True(command.IsComplete); Assert.Contains(command.Clause.Args, argument => argument.Resolved == "C:/work/a,b"); } @@ -402,14 +398,16 @@ public void Mixed_group_and_substitution_depth_overflow_fails_closed() } [Fact] - public void PowerShell_host_wrapper_pipeline_fails_without_child_identity_proof() + public void PowerShell_host_wrapper_pipeline_retains_static_authored_commands() { var result = Parse( "pwsh -Command \"Get-Item x | Select-Object Name; Get-Date\""); - Assert.True(result.IsUnparseable); - Assert.Empty(result.Commands); - Assert.Empty(result.Clauses); + Assert.False(result.IsUnparseable, result.UnparseableReason); + Assert.Equal( + new[] { "Get-Item", "Select-Object", "Get-Date" }, + result.Commands.Select(CommandVerb)); + Assert.All(result.Commands, command => Assert.True(command.IsComplete)); } [Fact] @@ -499,8 +497,8 @@ public void Terminal_outer_redirect_keeps_exact_element_span_on_last_wrapped_lea var result = Parse(source); Assert.Equal(2, result.Commands.Count); - Assert.False(result.Commands[0].IsComplete); - Assert.False(result.Commands[1].IsComplete); + Assert.True(result.Commands[0].IsComplete); + Assert.True(result.Commands[1].IsComplete); var last = result.Clauses[1]; Assert.Single(last.Redirects); var redirect = last.Elements.Last(); @@ -541,7 +539,7 @@ public void Exact_static_host_prefix_still_allows_structural_recursion() var wrapper = Assert.IsType(Assert.Single(result.Syntax.Statements)); Assert.Equal(ShellGroupKind.IsolatedScope, wrapper.GroupKind); - Assert.False(Assert.Single(result.Commands).IsComplete); + Assert.True(Assert.Single(result.Commands).IsComplete); Assert.Equal("Get-Date", Assert.Single(result.Clauses).Verb.Tokens[0]); } @@ -1203,10 +1201,11 @@ public void Nested_encoded_wrapper_redirect_uses_intermediate_invocation_scope() } [Theory] - [InlineData("/usr/bin/my-tool ~")] - [InlineData("./tool.ps1 ~")] - public void Encoded_wrapper_cannot_prove_path_shaped_command_binding( - string payload) + [InlineData("/usr/bin/my-tool ~", "C:/Users/test")] + [InlineData("./tool.ps1 ~", "~")] + public void Encoded_wrapper_retains_authored_path_shaped_command_binding( + string payload, + string expected) { var result = ParseIsolatedWithHome( $"pwsh -EncodedCommand {Encode(payload)}", @@ -1214,8 +1213,25 @@ public void Encoded_wrapper_cannot_prove_path_shaped_command_binding( var command = result.Commands.Last(); var effective = Assert.Single(command.EffectiveArguments); - Assert.Equal(ShellValueDomainKind.Unknown, effective.Value.Kind); - Assert.False(command.IsComplete); + Assert.Equal(ShellValueDomainKind.Exact, effective.Value.Kind); + Assert.Equal(expected, Assert.Single(effective.Value.Values)); + Assert.True(command.IsComplete); + } + + [Theory] + [InlineData("/usr/bin/my-tool ~", "C:/Users/test")] + [InlineData("./tool.ps1 ~", "~")] + public void Default_state_retains_authored_path_shaped_command_binding( + string source, + string expected) + { + var result = ParseWithHome(source, "C:/Users/test"); + + var command = Assert.Single(result.Commands); + var effective = Assert.Single(command.EffectiveArguments); + Assert.Equal(ShellValueDomainKind.Exact, effective.Value.Kind); + Assert.Equal(expected, Assert.Single(effective.Value.Values)); + Assert.True(command.IsComplete); } [Theory] @@ -1279,28 +1295,29 @@ public void Rebound_state_mutator_invalidates_all_later_state_proofs() } [Fact] - public void Unknown_initial_receiver_invalidates_later_state_without_hiding_leaves() + public void Ambient_receiver_uncertainty_does_not_poison_later_authored_facts() { var result = ParseUnknown("Invoke-Custom; Get-Content relative.txt"); + Assert.All(result.Commands, command => Assert.True(command.IsComplete)); var command = result.Commands.Last(); - Assert.False(command.IsComplete); - Assert.Equal(ShellValueDomainKind.Unknown, command.WorkingDirectory.Kind); - Assert.Contains(command.Clause.Args, argument => - argument.Raw == "relative.txt" && argument.Resolved is null); + Assert.Equal(ShellValueDomainKind.Exact, command.WorkingDirectory.Kind); Assert.Contains(command.Clause.Args, argument => - argument.IsCwdAttribution && argument.Raw == ""); + argument.Raw == "relative.txt" && + argument.Resolved == "C:/work/relative.txt"); + Assert.DoesNotContain(command.Clause.Args, argument => + argument.IsCwdAttribution); } [Fact] - public void Initial_state_contract_controls_authorization_identity_completeness() + public void Initial_state_contract_does_not_control_authored_completeness() { var unknown = ParseUnknown("Write-Output victim.txt"); var isolated = Parse("Write-Output victim.txt"); Assert.Single(unknown.Clauses); Assert.Single(isolated.Clauses); - Assert.False(Assert.Single(unknown.Commands).IsComplete); + Assert.True(Assert.Single(unknown.Commands).IsComplete); Assert.True(Assert.Single(isolated.Commands).IsComplete); } @@ -1312,11 +1329,14 @@ public void Encoded_unknown_receiver_keeps_later_structure_visible() "C:/Users/test"); var command = result.Commands.Last(); - Assert.False(command.IsComplete); + Assert.True(command.IsComplete); Assert.Equal( - ShellValueDomainKind.Unknown, + ShellValueDomainKind.Exact, Assert.Single(command.Redirects).Target.Kind); - Assert.True(Assert.Single(command.Clause.Redirects).IsDynamicSkip); + Assert.Equal( + "C:/work/relative.txt", + Assert.Single(Assert.Single(command.Redirects).Target.Values)); + Assert.False(Assert.Single(command.Clause.Redirects).IsDynamicSkip); } [Theory] @@ -1490,15 +1510,17 @@ public void Unqualified_hyphenated_command_binding_remains_unknown(string source } [Theory] - [InlineData("curl ~")] - [InlineData("Get-Content ~")] - public void Unknown_initial_state_does_not_assume_unqualified_command_binding( - string source) + [InlineData("curl ~", "C:/Users/test")] + [InlineData("Get-Content ~", "~")] + public void Default_state_uses_parser_owned_command_binding( + string source, + string expected) { var result = ParseWithHome(source, "C:/Users/test"); var effective = Assert.Single(Assert.Single(result.Commands).EffectiveArguments); - Assert.Equal(ShellValueDomainKind.Unknown, effective.Value.Kind); + Assert.Equal(ShellValueDomainKind.Exact, effective.Value.Kind); + Assert.Equal(expected, Assert.Single(effective.Value.Values)); } [Fact] diff --git a/tools/PwshCorpusTool/CorpusManifest.cs b/tools/PwshCorpusTool/CorpusManifest.cs index 10fa02d..2aba587 100644 --- a/tools/PwshCorpusTool/CorpusManifest.cs +++ b/tools/PwshCorpusTool/CorpusManifest.cs @@ -426,8 +426,8 @@ private static string NestIex(string inner, int depth) "pwsh -Command with a quoted-string payload; inner clause surfaces wrapped."), E("recursion_short_c", "pwsh -c \"Get-Date\"", "The short -c flag is recognized as -Command."), - Oos("recursion_command_pipeline", "pwsh -Command \"gci | rm\"", - "An unconstrained -Command child pipeline fails atomically because stage identities are not proved."), + E("recursion_command_pipeline", "pwsh -Command \"gci | rm\"", + "A static -Command child pipeline retains both authored stages."), E("recursion_command_scriptblock", "pwsh -Command { Get-Date }", "pwsh -Command with a script-block payload."), E("recursion_command_bare", "pwsh -Command Remove-Item C:\\tmp\\x", @@ -445,8 +445,7 @@ private static string NestIex(string inner, int depth) Enc("recursion_encoded_remove", "Remove-Item C:\\tmp\\x", "-EncodedCommand decoding to a Remove-Item clause."), Enc("recursion_encoded_pipeline", "Get-Process | Stop-Process", - "An unconstrained decoded child pipeline fails atomically because stage identities are not proved.", - oos: true), + "A static decoded child pipeline retains both authored stages."), Enc("recursion_encoded_controlflow", "if ($true) { Get-Date }", "An -EncodedCommand payload that decodes to control flow — inner unparseable propagates.", oos: true), E("recursion_encoded_nested", "pwsh -EncodedCommand " + B64("Get-Date"), @@ -814,7 +813,7 @@ private static string NestIex(string inner, int depth) "A standalone subexpression exposes its body without inventing an invocation."), S("v03_substitution_call_operator", "& $(Write-Output Get-Date) argument", - "The call operator exposes the producer and retains an incomplete dynamic invocation."), + "The call operator exposes the producer while the computed invocation remains incomplete."), S("v03_substitution_current_scope_cwd", "Write-Output $(Set-Location C:\\sensitive && Get-Location); Get-Item child.txt", "A subexpression success continuation sees the target while its failure-aware outer join stays unknown."), @@ -826,7 +825,7 @@ private static string NestIex(string inner, int depth) "An expanding here-string exposes its command subexpression."), S("v03_substitution_dynamic_host_payload", "pwsh -Command \"Write-Output $(Get-Date)\"", - "Parent-scope expansion remains visible beside an incomplete outer pwsh host."), + "Parent-scope expansion remains visible beside the complete decoded authored command."), S("v03_substitution_static_host_payload", "pwsh -Command 'Write-Output $(Get-Date)'", "A literal child payload is decoded and its child-scope substitution is exposed."), @@ -900,7 +899,7 @@ private static string NestIex(string inner, int depth) // ---- v0.3 foreach structure ---- V("v03_foreach_literal_array", "foreach ($f in @('a.txt', 'b.txt')) { Remove-Item -LiteralPath $f }", - "A static array exposes the loop body while binding-dependent facts remain incomplete."), + "A static array exposes the loop body while policy-sensitive binding facts remain strict."), V("v03_foreach_pipeline_iterator", "foreach ($f in Get-ChildItem C:\\input) { Remove-Item -LiteralPath $f }", "Iterator commands and loop-body commands remain distinct authored regions."), @@ -984,7 +983,7 @@ private static string NestIex(string inner, int depth) "The internal visit plan retains a,b,a while the body exposes the distinct finite set and the current-scope continuation sees final a."), VI("v03_foreach_empty_preserves_cwd", "foreach ($f in @()) { Set-Location C:\\tmp }; Get-Location", - "The unreachable body stays visible and incomplete, performs no state transition, and cannot leak parser-time cwd attribution into the continuation."), + "The unreachable authored body stays visible and complete, performs no state transition, and cannot leak parser-time cwd attribution into the continuation."), VI("v03_foreach_location_failure_join", "foreach ($d in @('C:\\a', 'C:\\b')) { Set-Location $d }; Get-ChildItem file.txt", "Every candidate is evaluated, but Set-Location failure and divergent successful targets force the body-input join and continuation cwd to Unknown."), @@ -1107,13 +1106,13 @@ private static string NestIex(string inner, int depth) "Encoded child syntax retains outer redirect provenance across a joined cwd."), VI("v03_redirect_unreachable_wrapper_binding", "foreach ($f in @()) { pwsh -Command 'Get-Date' > $f }", - "An unreachable wrapper redirect stays visible, Unknown, and incomplete rather than borrowing a loop value."), + "An unreachable wrapper command stays complete while its redirect remains Unknown rather than borrowing a loop value."), VI("v03_redirect_unreachable_relative_target", "foreach ($x in @()) { Write-Output x > relative.txt }", "An unreachable relative redirect cannot retain the parser working directory as an exact target."), VI("v03_redirect_unreachable_absolute_target", "foreach ($x in @()) { Write-Output x > C:\\fixed.txt }", - "A cwd-independent absolute redirect target remains exact even on an unreachable incomplete occurrence."), + "A cwd-independent absolute redirect target remains exact on an unreachable complete authored occurrence."), VI("v03_redirect_wrapper_parent_binding", "foreach ($f in @('one.txt', 'two.txt')) { pwsh -Command 'Get-Date' > $f }", "The parent shell resolves an outer wrapper redirect from its bounded loop binding before child launch."), @@ -1308,10 +1307,10 @@ private static string NestIex(string inner, int depth) "A redirect between nested decoded hosts resolves in the intermediate invocation rather than the deepest child."), VIE("v03_encoded_explicit_native_binding", "pwsh -EncodedCommand " + B64("/usr/bin/my-tool ~"), - "An explicit executable path supplies a native-binding candidate, but an unconstrained decoded child cannot prove it was not shadowed."), + "An explicit executable path supplies authored native-binding semantics without inspecting ambient child state."), VIE("v03_encoded_explicit_script_binding", "pwsh -EncodedCommand " + B64("./tool.ps1 ~"), - "An explicit script path supplies a PowerShell-binding candidate, but an unconstrained decoded child cannot prove it was not shadowed."), + "An explicit script path supplies authored PowerShell-binding semantics without inspecting ambient child state."), VIE("v03_unqualified_ps1_alias_binding_unknown", "Set-Alias foo.ps1 Write-Output; foo.ps1 ~", "An unqualified .ps1-shaped name can resolve to a mutated alias, so its argument binding remains Unknown."), @@ -1345,11 +1344,11 @@ private static string NestIex(string inner, int depth) "An uncontrolled decoded-host profile can mutate USERPROFILE before the payload executes."), VE("v03_unknown_receiver_preserves_compatibility_state", "Invoke-Custom; Get-Content relative.txt", - "Ambient uncertainty preserves both v0.2 leaves while the first unproved invocation invalidates the following authorization cwd and path facts."), + "Ambient uncertainty preserves both complete authored leaves and does not poison parser-owned cwd or path facts."), VIE("v03_encoded_unknown_receiver_preserves_compatibility_state", "pwsh -EncodedCommand " + B64( "foo; Write-Output hi > relative.txt"), - "An unconstrained decoded receiver remains visible and incomplete; its first unproved invocation makes the following redirect and cwd facts unknown."), + "A static decoded receiver remains visible and complete without poisoning the following authored redirect or cwd facts."), Oos("v03_unknown_receiver_pipeline_fails_atomically", "Invoke-Custom { Get-Item x | Select-Object Name }", "An unknown receiver's unproved interior pipeline fails atomically after the unknown-state transition."),