diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md index b95cbf3..6d25979 100644 --- a/IMPLEMENTATION_PLAN.md +++ b/IMPLEMENTATION_PLAN.md @@ -17,7 +17,7 @@ priorities. > below). What remains is the downstream Netclaw integration, which needs > actions outside this repository. -- [ ] **v0.3 prerelease consumer-API correction.** Preserve the stable v0.2 +- [x] **v0.3 prerelease consumer-API correction.** Preserve the stable v0.2 API and the `Syntax` / `Commands` / `Clauses` ingestion lanes, but replace the alpha-only sparse coordinate and public property-bag model before stable v0.3. Return one parser-owned analyzed argument per authored @@ -25,7 +25,10 @@ priorities. reference actual ancestor nodes, and remove public syntax vocabulary that stable v0.3 never emits. Update the OpenSpec and source mocks first; then update implementation, snapshots, corpus DTOs, consumer guide, - README, and Netclaw together. No compatibility shim for 0.3 alphas. + README, and Netclaw together. No compatibility shim for 0.3 alphas. The + library, specifications, snapshots, corpus DTOs, generated expectations, + README, consumer guide, and prerelease migration notes are synchronized; + Netclaw migration remains the next downstream item. - [x] **v0.3 host-selected grammar and PowerShell dialect — library slice.** The executor selects one top-level parser; Bash never cross-parses `pwsh` payloads and @@ -37,7 +40,7 @@ priorities. PowerShell 7.6.4 oracle on Ubuntu and Windows, plus native Windows PowerShell 5.1 discovery and its dialect-routed oracle on Windows. -- [ ] **v0.3 native-Windows Netclaw integration.** Pass the exact selected +- [x] **v0.3 native-Windows Netclaw integration.** Pass the exact selected shell through Netclaw's executor, approval policy, and model context; prefer a compatible `pwsh.exe`, fall back to `powershell.exe`, and reparse and reauthorize if executable selection changes. @@ -132,7 +135,10 @@ priorities. - [x] **Consumer guide.** Added `docs/CONSUMER_GUIDE.md` with the shell-neutral security-consumer algorithm, Bash and PowerShell guidance, worked public use cases, and immutable permalinks to Netclaw's production - integration. Linked it from the README and aligned stale PowerShell + integration. Added compact input-to-result-to-policy examples for command + occurrences, attached arguments, bounded and zero-or-more loops, cwd + propagation, file and descriptor redirects, substitutions, and safe-fail + results. Linked it from the README and aligned stale PowerShell prerelease/status wording in the public project docs. - [x] **Issue #52 — hyphenated PowerShell parameters/native options.** Preserve internal hyphens, apply bash-compatible native @@ -157,9 +163,9 @@ priorities. ### 16. Netclaw v0.2.0 integration — SPEC.PWSH §17 #9 -- [ ] Netclaw consumes the v0.2.0 package; absorbs the `Clause` rename +- [x] Netclaw consumes the v0.2.0 package; absorbs the `Clause` rename *(separate repository — cannot be done here)* -- [ ] ≥1 Netclaw integration test exercises a real PowerShell corpus entry +- [x] ≥1 Netclaw integration test exercises a real PowerShell corpus entry through the live matcher and gets the expected gate decision ### 17. v0.3 structured shell analysis contract — issue #72 @@ -489,7 +495,7 @@ priorities. effects, publishes synchronous/once only for one proved target, and publishes concurrent timing for multiple targets or enabled asynchronous switches while keeping dynamic cardinality fail closed. -- [ ] Deliver typed PowerShell script-block execution regions before calling +- [x] Deliver typed PowerShell script-block execution regions before calling tasks 7.5-7.7 complete. The corrected contract adds an execution-region syntax node with independent origin, phase, timing, and cardinality rather than a false shared/isolated scope flag. The inert additive public API skeleton, @@ -615,7 +621,7 @@ priorities. independence, semantic phase order, child process/runspace boundaries, boundaries and the fact that the in-process `Invoke-Command` parameter set does not support `-AsJob`. -- [ ] Complete the stable-v0.3 Bash `for ... in` and PowerShell `foreach` +- [x] Complete the stable-v0.3 Bash `for ... in` and PowerShell `foreach` vertical slices without gating release on a shared-analysis refactor. - [x] Publish `0.3.0-alpha` for the Netclaw migration gate. The bare SemVer tag published the NuGet package, symbol package, and GitHub prerelease from @@ -695,7 +701,7 @@ priorities. and reserved-form boundaries, including unmodeled `time`, negation, coprocess, and current-shell brace-group syntax, which fail closed rather than flattening nested execution into apparent ordinary verb chains. -- [ ] Promote the Bash command-resolution mutation cases into Netclaw's strict +- [x] Promote the Bash command-resolution mutation cases into Netclaw's strict allow/prompt/deny matrix before the downstream approval-fatigue gate. - [x] Add the separately tested Bash `<<<` here-string redirect slice with bounded operand analysis and trailing-newline semantics. Default and diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md index f1ce924..c972fd0 100644 --- a/PROJECT_CONTEXT.md +++ b/PROJECT_CONTEXT.md @@ -81,13 +81,18 @@ zero-native-deps .NET parser sized to what security gates actually need. the dialect-matched live oracle matrix (`pwsh` for PowerShell 7 and `powershell.exe` for Windows PowerShell 5.1 on Windows CI). -### v0.3 (contract design) +### v0.3 (implementation and downstream acceptance) - Add a closed, strongly typed syntax-node hierarchy while retaining existing `Clause` leaves. - Add a library-owned command-occurrence projection for security consumers so every potentially executable iterator, substitution, execution-region, and body command in the supported grammar is evaluated exactly once. +- Return one parser-owned analyzed argument for every authored non-cwd + compatibility argument. Closed value and redirect record families use + runtime type as their discriminant; ancestry and execution-region facts + reference the actual parser-owned source objects. Compatibility is required + against stable v0.2, not experimental v0.3 alpha surfaces. - Add typed PowerShell execution regions for direct call/dot-source blocks, synchronous callbacks, jobs/parallel runspaces, and initialization. Public origin/phase/timing/cardinality facts remain separate from @@ -114,9 +119,10 @@ zero-native-deps .NET parser sized to what security gates actually need. Existing conservative recognition may remain; unproved receivers still expose completely delimited bodies as incomplete regions and therefore remain fail closed. -- Treat `openspec/changes/v0-3-structured-shell-analysis/` and its paired design - corpus as the review authority until the accepted contract is synchronized - into `SPEC.md` and `SPEC.POWERSHELL.md` with the production API change. +- Treat `SPEC.md` and `SPEC.POWERSHELL.md` as the canonical synchronized + contract. The accepted OpenSpec change and paired design corpus retain the + design rationale and verification checklist until downstream acceptance and + archival. ### v0.2 explicit non-goals diff --git a/README.md b/README.md index 9ba6949..2e0dbf3 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,8 @@ foreach (var occurrence in parsed.Commands) var clause = occurrence.Clause; Console.WriteLine( $"{occurrence.ImmediateRole} {clause.Verb.Joined} " + - $"complete={occurrence.IsComplete} cwd={occurrence.WorkingDirectory.Kind}"); + $"complete={occurrence.IsComplete} " + + $"cwd={Describe(occurrence.WorkingDirectory)}"); foreach (var arg in clause.Args.Where(a => a.IsPath)) { @@ -91,19 +92,42 @@ foreach (var occurrence in parsed.Commands) Console.WriteLine($" {marker}: {arg.Resolved}"); } - foreach (var effective in occurrence.EffectiveArguments) + foreach (var argument in occurrence.Arguments) { Console.WriteLine( - $" element[{effective.ClauseElementIndex}]: {effective.Value.Kind}"); + $" {argument.Argument.Raw}: {Describe(argument.Value)}"); } foreach (var redirect in occurrence.Redirects) { - Console.WriteLine( - $" {redirect.Operation}: {redirect.Target.Kind} " + - $"complete={redirect.IsComplete}"); + Console.WriteLine($" {Describe(redirect)}"); } } + +static string Describe(ShellValueDomain value) => value switch +{ + ShellValueDomain.Exact exact => exact.Value, + ShellValueDomain.FiniteSet finite => string.Join(" | ", finite.Values), + ShellValueDomain.PathPattern pattern => pattern.Pattern, + ShellValueDomain.Unknown => "unknown", + _ => "unknown", +}; + +static string Describe(RedirectAnalysis redirect) => redirect switch +{ + FileRedirectAnalysis file => + $"{file.Mode} to {Describe(file.Target)} complete={file.IsComplete}", + DescriptorDuplicateRedirectAnalysis duplicate => + $"duplicate to fd {duplicate.TargetDescriptor}", + DescriptorMoveRedirectAnalysis move => + $"move to fd {move.TargetDescriptor}", + DescriptorCloseRedirectAnalysis => "close descriptor", + HereDocumentRedirectAnalysis => "heredoc stdin data", + HereStringRedirectAnalysis hereString => + $"here-string stdin data: {Describe(hereString.Data)}", + UnresolvedRedirectAnalysis => "unresolved redirect", + _ => "unknown redirect", +}; ``` ## Consumer guide @@ -131,9 +155,11 @@ public enum PwshDialect { Unknown, PowerShell7, WindowsPowerShell51 } public sealed record ParsedCommand { /* Source, Syntax, Commands, Clauses, IsUnparseable, … */ } public abstract record ShellSyntaxNode; -public sealed record CommandOccurrence { /* Clause, role, ancestry, effective values, cwd, redirects, completeness */ } -public sealed record ShellValueDomain { /* Exact, FiniteSet, Pattern, or Unknown */ } -public sealed record RedirectAnalysis { /* source, operation, target, heredoc facts, completeness */ } +public sealed record CommandOccurrence { /* Clause, role, ancestry, analyzed arguments, cwd, redirects, completeness */ } +public sealed record AnalyzedArgument { /* direct Arg + ClauseElement + ShellValueDomain join */ } +public abstract record ShellValueDomain; // nested Unknown, Exact, FiniteSet, PathPattern +public abstract record RedirectSource; // nested Unknown, Default, Descriptor, PowerShellAllStreams +public abstract record RedirectAnalysis; // file, descriptor, heredoc, here-string, or unresolved alternative public sealed record Clause { /* Operator, Verb, Args, Redirects, Elements, IsSubshell, IsCommandStringWrapped */ } public sealed record ClauseElement { /* Raw, Value, Role, source span, verb-relative position, path facts */ } public sealed record VerbChain { /* Tokens, Joined, CanonicalVerb, IsDynamic */ } diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8602fc6..8614914 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,16 +1,35 @@ #### Unreleased #### +## Changed + +- Replace the experimental v0.3 sparse `EffectiveArguments` coordinate overlay + with one parser-owned `AnalyzedArgument` per authored non-cwd argument. Each + entry directly references its `Arg`, source `ClauseElement`, and effective + value, including many-to-one inline option bindings. +- Replace the experimental value, redirect-source, and redirect-operation + property bags with closed record families intended for runtime type + matching. Ancestry frames now reference actual syntax nodes, and execution + regions reference their actual host argument. +- Remove alpha-only public syntax kinds, condition/branch vocabulary, analysis + limits, and other shapes the stable parser never emits. Every new v0.3 result + is parser-owned and every read-only list introduced by v0.3 is defensively + backed; stable v0.2 construction and list semantics remain unchanged. + ## Consumer migration - v0.3 security consumers authorize `ParsedCommand.Commands` and use `ParsedCommand.Syntax` only for display and diagnostics. The conservative v0.2 `Clauses` projection remains supported throughout v0.3, including every v0.3.x release; no removal version is scheduled. -- The additive v0.3 records and `ParsedCommand` members change generated record +- The new v0.3 records and `ParsedCommand` members change generated record equality, hashing, `ToString()`, and reflection-based serialization output. ShellSyntaxTree does not define a stable serialized wire format. Persisted results require a consumer-owned, versioned DTO or explicit serializer - mapping that fails closed on unknown node and enum values. + mapping that fails closed on unknown runtime alternatives and enum values. +- No source or binary compatibility is provided for `0.3.0-alpha.*` packages. + Stable v0.2 remains the compatibility boundary. Alpha consumers must migrate + to `CommandOccurrence.Arguments` and pattern-match the closed value and + redirect families; no aliases or obsolete adapters preserve the old model. - `PwshParserOptions.Dialect` is additive and defaults to `PowerShell7` for compatibility. Native Windows consumers select it only for a compatible PowerShell 7.6 host (`>=7.6.4` and `<7.7`) and select diff --git a/SPEC.POWERSHELL.md b/SPEC.POWERSHELL.md index 85c5fc3..ca5d9c4 100644 --- a/SPEC.POWERSHELL.md +++ b/SPEC.POWERSHELL.md @@ -1292,14 +1292,16 @@ about which stream produced it: | stream merge `N>&1` for `N` in `2`–`6`, or `*>&1` | `ErrOut` when `N` is `2`, else `Out`; `Target` carries `&1` verbatim with `IsDynamicSkip=true` | The table above remains the v0.2 `Redirect` compatibility mapping. v0.3 also -populates `RedirectAnalysis`: `RedirectSourceKind.PowerShellAllStreams` -preserves `*`, `Descriptor` preserves numeric streams, and `Operation` -distinguishes file output/append from static descriptor duplication. Static -descriptor operations are not path-relevant. PowerShell's grammar does not +populates the closed `RedirectAnalysis` family: +`RedirectSource.PowerShellAllStreams` preserves `*`, +`RedirectSource.Descriptor` preserves numeric streams, and runtime alternatives +distinguish `FileRedirectAnalysis` output/append from static +`DescriptorDuplicateRedirectAnalysis`. Descriptor alternatives are not paths. +PowerShell's grammar does not admit descriptor close, move, computed merge targets, or file input redirection; those spellings make the whole parse unparseable. `$null` and `${null}` -remains an incomplete explicit redirect until the public operation vocabulary +remain an incomplete explicit redirect until the public operation vocabulary has a discard-sink representation; consumers must continue to fail closed. --- diff --git a/SPEC.md b/SPEC.md index 661b0e5..df14131 100644 --- a/SPEC.md +++ b/SPEC.md @@ -205,16 +205,10 @@ public sealed record CommandListSyntax : ShellSyntaxNode { ... } public sealed record CommandListItemSyntax { ... } public sealed record GroupSyntax : ShellSyntaxNode { ... } public sealed record ForEachSyntax : ShellSyntaxNode { ... } -public sealed record LoopBindingSyntax { ... } public sealed record ShellSourceFragment { ... } -public sealed record ConditionLoopSyntax : ShellSyntaxNode { ... } -public sealed record ConditionalSyntax : ShellSyntaxNode { ... } -public sealed record ConditionalBranchSyntax : ShellSyntaxNode { ... } public sealed record CommandSubstitutionSyntax : ShellSyntaxNode { ... } public sealed record ExecutionRegionSyntax : ShellSyntaxNode { ... } -public enum ShellSyntaxKind { ... } public enum ShellGroupKind { ... } -public enum ConditionLoopKind { ... } public enum ExecutionRegionOrigin { ... } public enum ExecutionRegionPhase { ... } public enum ExecutionRegionTiming { ... } @@ -223,23 +217,28 @@ public enum ExecutionRegionCardinality { ... } // v0.3 authorization and bounded-analysis projections — see §3. public sealed record CommandOccurrence { ... } public sealed record CommandAncestryFrame { ... } -public sealed record EffectiveArgument { ... } -public sealed record ShellValueDomain { ... } -public sealed record RedirectAnalysis { ... } +public sealed record AnalyzedArgument { ... } +public abstract record ShellValueDomain { ... } +public abstract record RedirectAnalysis { ... } public sealed record HereDocumentAnalysis { ... } -public sealed record RedirectSource { ... } +public abstract record RedirectSource { ... } +public sealed record FileRedirectAnalysis : RedirectAnalysis { ... } +public sealed record UnresolvedRedirectAnalysis : RedirectAnalysis { ... } +public sealed record DescriptorDuplicateRedirectAnalysis : RedirectAnalysis { ... } +public sealed record DescriptorMoveRedirectAnalysis : RedirectAnalysis { ... } +public sealed record DescriptorCloseRedirectAnalysis : RedirectAnalysis { ... } +public sealed record HereDocumentRedirectAnalysis : RedirectAnalysis { ... } +public sealed record HereStringRedirectAnalysis : RedirectAnalysis { ... } public enum CommandOccurrenceRole { ... } public enum CommandAncestryRegion { ... } -public enum ShellValueDomainKind { ... } +public enum FileRedirectMode { ... } public enum HereDocumentExpansionMode { ... } -public enum RedirectSourceKind { ... } -public enum RedirectOperation { ... } -public static class ShellAnalysisLimits { ... } ``` -`ConditionLoopSyntax`, `ConditionalSyntax`, and `ConditionalBranchSyntax` are -reserved v0.3 structural vocabulary. The stable-v0.3 parsers do not emit them; -condition-loop and branch grammar remains fail closed until a later release. +Stable v0.3 exposes only structural types the parsers can emit. Condition-loop +and branch grammar remains fail closed and reserves no public type or enum +member. Every new v0.3 result type is parser-owned; its constructor and result +setters are not public. The stable v0.2 constructors and setters are unchanged. That's the entire public API. **Everything else is internal.** The lexer, parser internals, verb tables, resolver — all implementation detail. @@ -263,13 +262,13 @@ public sealed record ParsedCommand /// source ranges; decoded wrapper nodes report unavailable ranges unless /// an exact outer mapping exists. /// - public ShellBlockSyntax Syntax { get; init; } = new(); + public ShellBlockSyntax Syntax { get; internal init; } = new(); /// /// Canonical authorization projection containing every authored simple /// command that may execute exactly once, in deterministic source order. /// - public IReadOnlyList Commands { get; init; } = []; + public IReadOnlyList Commands { get; internal init; } = []; /// /// Conservative v0.2 compatibility projection. Existing simple-command @@ -424,65 +423,55 @@ for structure, explanation, display, and specialized analysis. public abstract record ShellSyntaxNode { private protected ShellSyntaxNode() { } + private protected abstract object LibraryOwnership { get; } - public abstract ShellSyntaxKind Kind { get; } - public int? SourceStart { get; init; } - public int? SourceLength { get; init; } -} - -public enum ShellSyntaxKind -{ - Unknown, - Block, - SimpleCommand, - Pipeline, - CommandList, - Group, - ForEach, - ConditionLoop, - Conditional, - ConditionalBranch, - CommandSubstitution, - ExecutionRegion, + public int? SourceStart { get; internal init; } + public int? SourceLength { get; internal init; } } public sealed record ShellBlockSyntax : ShellSyntaxNode { - public override ShellSyntaxKind Kind => ShellSyntaxKind.Block; - public IReadOnlyList Statements { get; init; } = []; + internal ShellBlockSyntax() { } + private protected override object LibraryOwnership => this; + public IReadOnlyList Statements { get; internal init; } = []; } public sealed record SimpleCommandSyntax : ShellSyntaxNode { - public override ShellSyntaxKind Kind => ShellSyntaxKind.SimpleCommand; - public Clause Clause { get; init; } = new(); - public IReadOnlyList Substitutions { get; init; } = []; - public IReadOnlyList ExecutionRegions { get; init; } = []; + internal SimpleCommandSyntax() { } + private protected override object LibraryOwnership => this; + public Clause Clause { get; internal init; } = new(); + public IReadOnlyList Substitutions { get; internal init; } = []; + public IReadOnlyList ExecutionRegions { get; internal init; } = []; } public sealed record PipelineSyntax : ShellSyntaxNode { - public override ShellSyntaxKind Kind => ShellSyntaxKind.Pipeline; - public IReadOnlyList Stages { get; init; } = []; + internal PipelineSyntax() { } + private protected override object LibraryOwnership => this; + public IReadOnlyList Stages { get; internal init; } = []; } public sealed record CommandListSyntax : ShellSyntaxNode { - public override ShellSyntaxKind Kind => ShellSyntaxKind.CommandList; - public IReadOnlyList Items { get; init; } = []; + internal CommandListSyntax() { } + private protected override object LibraryOwnership => this; + public IReadOnlyList Items { get; internal init; } = []; } public sealed record CommandListItemSyntax { - public CompoundOperator Operator { get; init; } - public ShellSyntaxNode Command { get; init; } = new ShellBlockSyntax(); + internal CommandListItemSyntax() { } + public CompoundOperator Operator { get; internal init; } + public ShellSyntaxNode Command { get; internal init; } = null!; } public sealed record GroupSyntax : ShellSyntaxNode { - public override ShellSyntaxKind Kind => ShellSyntaxKind.Group; - public ShellGroupKind GroupKind { get; init; } - public ShellBlockSyntax Body { get; init; } = new(); + internal GroupSyntax() { } + private protected override object LibraryOwnership => this; + public ShellGroupKind GroupKind { get; internal init; } + public ShellBlockSyntax Body { get; internal init; } = null!; } public enum ShellGroupKind @@ -494,70 +483,40 @@ public enum ShellGroupKind public sealed record ForEachSyntax : ShellSyntaxNode { - public override ShellSyntaxKind Kind => ShellSyntaxKind.ForEach; - public LoopBindingSyntax Binding { get; init; } = new(); - public ShellSourceFragment Iterable { get; init; } = new(); - public ShellBlockSyntax IteratorCommands { get; init; } = new(); - public ShellBlockSyntax Body { get; init; } = new(); -} - -public sealed record LoopBindingSyntax -{ - public string Name { get; init; } = ""; - public ShellSourceFragment Source { get; init; } = new(); + internal ForEachSyntax() { } + private protected override object LibraryOwnership => this; + public string BindingName { get; internal init; } = ""; + public ShellSourceFragment BindingSource { get; internal init; } = null!; + public ShellSourceFragment Iterable { get; internal init; } = null!; + public ShellBlockSyntax IteratorCommands { get; internal init; } = null!; + public ShellBlockSyntax Body { get; internal init; } = null!; } public sealed record ShellSourceFragment { - public string Raw { get; init; } = ""; - public int? SourceStart { get; init; } - public int? SourceLength { get; init; } -} - -public sealed record ConditionLoopSyntax : ShellSyntaxNode -{ - public override ShellSyntaxKind Kind => ShellSyntaxKind.ConditionLoop; - public ConditionLoopKind LoopKind { get; init; } - public ShellBlockSyntax Condition { get; init; } = new(); - public ShellBlockSyntax Body { get; init; } = new(); -} - -public enum ConditionLoopKind -{ - Unknown, - While, - Until, -} - -public sealed record ConditionalSyntax : ShellSyntaxNode -{ - public override ShellSyntaxKind Kind => ShellSyntaxKind.Conditional; - public IReadOnlyList Branches { get; init; } = []; - public ShellBlockSyntax? Else { get; init; } -} - -public sealed record ConditionalBranchSyntax : ShellSyntaxNode -{ - public override ShellSyntaxKind Kind => ShellSyntaxKind.ConditionalBranch; - public ShellBlockSyntax Condition { get; init; } = new(); - public ShellBlockSyntax Body { get; init; } = new(); + internal ShellSourceFragment() { } + public string Raw { get; internal init; } = ""; + public int? SourceStart { get; internal init; } + public int? SourceLength { get; internal init; } } public sealed record CommandSubstitutionSyntax : ShellSyntaxNode { - public override ShellSyntaxKind Kind => ShellSyntaxKind.CommandSubstitution; - public ShellBlockSyntax Body { get; init; } = new(); + internal CommandSubstitutionSyntax() { } + private protected override object LibraryOwnership => this; + public ShellBlockSyntax Body { get; internal init; } = null!; } public sealed record ExecutionRegionSyntax : ShellSyntaxNode { - public override ShellSyntaxKind Kind => ShellSyntaxKind.ExecutionRegion; - public ExecutionRegionOrigin Origin { get; init; } - public int? HostClauseElementIndex { get; init; } - public ExecutionRegionPhase Phase { get; init; } - public ExecutionRegionTiming Timing { get; init; } - public ExecutionRegionCardinality Cardinality { get; init; } - public ShellBlockSyntax Body { get; init; } = new(); + internal ExecutionRegionSyntax() { } + private protected override object LibraryOwnership => this; + public ExecutionRegionOrigin Origin { get; internal init; } + public ClauseElement? HostArgument { get; internal init; } + public ExecutionRegionPhase Phase { get; internal init; } + public ExecutionRegionTiming Timing { get; internal init; } + public ExecutionRegionCardinality Cardinality { get; internal init; } + public ShellBlockSyntax Body { get; internal init; } = null!; } public enum ExecutionRegionOrigin @@ -604,21 +563,34 @@ PowerShell expressions share a grammar. Direct-source nodes have exact ranges into `ParsedCommand.Source`. Nodes lifted from decoded, escaped, or encoded wrapper content use null ranges unless an exact outer mapping exists. -Every v0.3 enum reserves zero as `Unknown`. Consumers fail closed on -`Unknown` or an unrecognized numeric value when the fact affects policy. +Every new v0.3 result-type constructor and new v0.3 member setter is +library-owned. Every `IReadOnlyList` introduced by v0.3 is a defensive +snapshot, not a mutable array or list that a consumer can cast and change. +Stable v0.2 construction, setters, and list semantics remain unchanged. +Runtime type is the discriminant for each closed +record family; consumers keep a default fail-closed switch arm for future +library-owned alternatives. Compatibility is required against stable v0.2, +not against any experimental `0.3.0-alpha.*` surface. + +Every v0.3 enum whose model admits an unknown state reserves zero as +`Unknown`. Consumers fail closed on `Unknown` or an unrecognized numeric value +when the fact affects policy. `FileRedirectMode` has no `Unknown` member: +unresolved operations use `UnresolvedRedirectAnalysis`, and only the library +can construct a `FileRedirectAnalysis` with a known mode. ### Command occurrences and bounded values (v0.3) ```csharp public sealed record CommandOccurrence { - public Clause Clause { get; init; } = new(); - public CommandOccurrenceRole ImmediateRole { get; init; } - public IReadOnlyList Ancestry { get; init; } = []; - public IReadOnlyList EffectiveArguments { get; init; } = []; - public ShellValueDomain WorkingDirectory { get; init; } = ShellValueDomain.Unknown; - public IReadOnlyList Redirects { get; init; } = []; - public bool IsComplete { get; init; } + internal CommandOccurrence() { } + public Clause Clause { get; internal init; } = new(); + public CommandOccurrenceRole ImmediateRole { get; internal init; } + public IReadOnlyList Ancestry { get; internal init; } = []; + public IReadOnlyList Arguments { get; internal init; } = []; + public ShellValueDomain WorkingDirectory { get; internal init; } = null!; + public IReadOnlyList Redirects { get; internal init; } = []; + public bool IsComplete { get; internal init; } } public enum CommandOccurrenceRole @@ -626,21 +598,18 @@ public enum CommandOccurrenceRole Unknown, Ordinary, PipelineStage, - Condition, Iterator, LoopBody, - Branch, Substitution, ExecutionRegion, } public sealed record CommandAncestryFrame { - public ShellSyntaxKind AncestorKind { get; init; } - public CommandAncestryRegion Region { get; init; } - public int? ChildIndex { get; init; } - public int? SourceStart { get; init; } - public int? SourceLength { get; init; } + internal CommandAncestryFrame() { } + public ShellSyntaxNode Ancestor { get; internal init; } = null!; + public CommandAncestryRegion Region { get; internal init; } + public int? ChildIndex { get; internal init; } } public enum CommandAncestryRegion @@ -652,41 +621,34 @@ public enum CommandAncestryRegion GroupBody, Iterator, LoopBody, - Condition, - Branch, Substitution, ExecutionRegion, } -public sealed record EffectiveArgument +public sealed record AnalyzedArgument { - public int ClauseElementIndex { get; init; } = -1; - public ShellValueDomain Value { get; init; } = ShellValueDomain.Unknown; -} - -public sealed record ShellValueDomain -{ - public static ShellValueDomain Unknown { get; } = new(); - - public ShellValueDomainKind Kind { get; init; } - public IReadOnlyList Values { get; init; } = []; - public string? Pattern { get; init; } - public string? CoveringDirectory { get; init; } + internal AnalyzedArgument() { } + public Arg Argument { get; internal init; } = null!; + public ClauseElement Element { get; internal init; } = null!; + public ShellValueDomain Value { get; internal init; } = null!; } -public enum ShellValueDomainKind +public abstract record ShellValueDomain { - Unknown, - Exact, - FiniteSet, - Pattern, -} + private protected ShellValueDomain() { } + private protected abstract object LibraryOwnership { get; } -public static class ShellAnalysisLimits -{ - public static int MaxValueCandidates => 32; - public static int MaxStructuralNesting => 16; - public static int MaxWrapperRecursionDepth => 5; + public sealed record Unknown : ShellValueDomain { ... } + public sealed record Exact : ShellValueDomain { public string Value { get; } } + public sealed record FiniteSet : ShellValueDomain + { + public IReadOnlyList Values { get; } + } + public sealed record PathPattern : ShellValueDomain + { + public string Pattern { get; } + public string CoveringDirectory { get; } + } } ``` @@ -717,11 +679,11 @@ that a proved or conservatively unknown command binding may execute. The unchanged host `Clause` retains its authored script-block `DynamicSkip` argument. A direct PowerShell `& {}` or `. {}` region appears as a statement, carries `Origin=DirectCall` or `Origin=DotSource`, has -`HostClauseElementIndex=null`, and creates no synthetic occurrence for the +`HostArgument=null`, and creates no synthetic occurrence for the invocation operator. A command-owned region carries `Origin=CommandArgument` -and the non-negative index of its script-block token in the host -`Clause.Elements`. Consumers use `Origin`, rather than reparsing source text, -to distinguish the different direct-invocation state semantics. +and `HostArgument` references the exact script-block `ClauseElement` in the +host command. Consumers use `Origin`, rather than reparsing source text, to +distinguish the different direct-invocation state semantics. Execution-region origin, phase, timing, and cardinality are independent structural facts. Attached regions retain authored script-block order even when a @@ -745,49 +707,49 @@ zero-based `ChildIndex` in the structural collection that owns the `CommandSubstitutionSyntax`. For a simple command this is its `Substitutions` collection; for an iterator it is the containing iterator-command collection. -Each ancestry frame describes the relationship from its `AncestorKind` to the -next node on the path. The root block uses `Root`; non-root blocks and command +Each ancestry frame references the actual `Ancestor` node and describes its +relationship to the next node on the path. The root block uses `Root`; non-root blocks and command lists use `Statement`; pipelines use `PipelineStage`; groups use `GroupBody`; -foreach nodes use `Iterator` or `LoopBody`; condition loops use `Condition` or -`LoopBody`; conditionals use `Branch`; conditional-branch nodes use -`Condition` or `Branch`; command substitutions use `Substitution`; and +foreach nodes use `Iterator` or `LoopBody`; command substitutions use `Substitution`; and execution regions use `ExecutionRegion`. -Repeated children use their zero-based authored index. The `else` child uses -the branch count, placing it after every condition/body pair. Frame source -ranges belong to the ancestor. Blocks, command lists, and groups retain the +Repeated children use their zero-based authored index. Source ranges are read +from the referenced ancestor. Blocks, command lists, and groups retain the incoming immediate role; pipeline stages, iterator/body regions, -condition/body regions, branches, substitutions, and execution regions replace -it with their nearer execution role. +substitutions, and execution regions replace it with their nearer execution role. Projection accepts only a parser-owned tree: a syntax-node or `Clause` reference cannot appear at two authored positions, node and fragment spans are either both unavailable or a non-negative start/length pair, and structural enum values consumed by the projector must be known. Empty blocks remain -valid, but empty pipelines, command lists, and conditionals are malformed. -Joined value domains, effective-argument coordinates, cwd facts, redirect +valid, but empty pipelines and command lists are malformed. +Joined value domains, analyzed-argument references, cwd facts, redirect coordinates, redirect shapes, and heredoc facts must satisfy their contracts. Any violation discards the partial `Commands` and `Clauses` collections and makes the outer parse unparseable; it is never published as a complete occurrence. -`EffectiveArgument.ClauseElementIndex` is a stable authored coordinate. The -analysis never mutates a compatibility `Arg` to hold loop-specific values. -An occurrence can be structurally complete while one effective value remains -`Unknown`; completeness and value precision are independent. +`Arguments` contains exactly one `AnalyzedArgument` for each non-cwd +`Clause.Args` entry in authored order. `Argument` and `Element` reference the +existing compatibility objects directly. Inline equals/colon forms may create +multiple arguments that share one source element. The analysis never mutates a +compatibility `Arg` to hold loop-specific values. An occurrence can be +structurally complete while one value remains `Unknown`; completeness and +value precision are independent. The parser emits only these value-domain combinations: -- `Unknown`: no values, pattern, or covering directory; -- `Exact`: exactly one value and no pattern fields; -- `FiniteSet`: 2–32 distinct values and no pattern fields; -- `Pattern`: no values, a non-empty pattern, and a non-empty covering directory. +- `Unknown`: no payload; +- `Exact`: exactly one non-null value; +- `FiniteSet`: 2–32 distinct non-null values; +- `PathPattern`: a non-empty pattern and non-empty covering directory. The parser does not execute commands, inspect runtime variables, enumerate the filesystem, or truncate an over-limit set and call it complete. A result with -33 or more candidates becomes `Unknown`. Structural depth starts at zero for -the root and increments on foreach loops, condition loops, conditionals, -groups, command substitutions, and execution regions; blocks, lists, -pipelines, branches, and +33 or more candidates becomes `Unknown`. The candidate cap of 32, structural +depth cap of 16, and wrapper-recursion cap of 5 are parser contracts, not +public tuning knobs. Structural depth starts at zero for the root and +increments on foreach loops, groups, command substitutions, and execution +regions; blocks, lists, pipelines, and simple-command leaves do not independently increment it. Exceeding 16 structural containers or 5 decoded-command wrapper recursions makes the entire result unparseable. @@ -943,76 +905,98 @@ Occurrence-specific redirect analysis is additive. The existing `Redirect` record remains the v0.2 compatibility leaf and is not reinterpreted. ```csharp -public sealed record RedirectAnalysis +public abstract record RedirectSource { - public int RedirectIndex { get; init; } = -1; - public RedirectSource Source { get; init; } = new(); - public RedirectOperation Operation { get; init; } - public int? TargetDescriptor { get; init; } - public ShellValueDomain Target { get; init; } = ShellValueDomain.Unknown; - public HereDocumentAnalysis? HereDocument { get; init; } - public bool IsPathRelevant { get; init; } - public bool IsComplete { get; init; } + private protected RedirectSource() { } + private protected abstract object LibraryOwnership { get; } + + public sealed record Unknown : RedirectSource { ... } + public sealed record Default : RedirectSource { ... } + public sealed record Descriptor : RedirectSource { public int Value { get; } } + public sealed record PowerShellAllStreams : RedirectSource { ... } } -public sealed record HereDocumentAnalysis +public abstract record RedirectAnalysis { - public ShellSourceFragment Delimiter { get; init; } = new(); - public ShellSourceFragment Body { get; init; } = new(); - public HereDocumentExpansionMode ExpansionMode { get; init; } - public bool StripLeadingTabs { get; init; } - public bool IsComplete { get; init; } + private protected RedirectAnalysis() { } + private protected abstract object LibraryOwnership { get; } + + public Redirect Authored { get; internal init; } = null!; + public RedirectSource Source { get; internal init; } = null!; + public bool IsComplete { get; internal init; } } -public enum HereDocumentExpansionMode +public sealed record FileRedirectAnalysis : RedirectAnalysis { - Unknown, - Literal, - Expand, + public FileRedirectMode Mode { get; } + public ShellValueDomain Target { get; internal init; } = null!; } -public sealed record RedirectSource +public sealed record UnresolvedRedirectAnalysis : RedirectAnalysis { ... } +public sealed record DescriptorDuplicateRedirectAnalysis : RedirectAnalysis { - public RedirectSourceKind Kind { get; init; } - public int? Descriptor { get; init; } + public int TargetDescriptor { get; internal init; } } - -public enum RedirectSourceKind +public sealed record DescriptorMoveRedirectAnalysis : RedirectAnalysis { - Unknown, - Default, - Descriptor, - PowerShellAllStreams, + public int TargetDescriptor { get; internal init; } +} +public sealed record DescriptorCloseRedirectAnalysis : RedirectAnalysis { ... } +public sealed record HereDocumentRedirectAnalysis : RedirectAnalysis +{ + public HereDocumentAnalysis Document { get; internal init; } = null!; +} +public sealed record HereStringRedirectAnalysis : RedirectAnalysis +{ + public ShellValueDomain Data { get; internal init; } = null!; } -public enum RedirectOperation +public enum FileRedirectMode { - Unknown, - FileInput, - FileOutput, - FileAppend, - DescriptorDuplicate, - DescriptorClose, - DescriptorMove, + Input, + Output, + Append, CombinedOutput, CombinedOutputAppend, - HereDocument, - HereString, } + +public sealed record HereDocumentAnalysis +{ + internal HereDocumentAnalysis() { } + public ShellSourceFragment Delimiter { get; internal init; } = null!; + public ShellSourceFragment Body { get; internal init; } = null!; + public HereDocumentExpansionMode ExpansionMode { get; internal init; } + public bool StripLeadingTabs { get; internal init; } + public bool IsComplete { get; internal init; } +} + +public enum HereDocumentExpansionMode +{ + Unknown, + Literal, + Expand, +} + ``` -`RedirectIndex` correlates to `Clause.Redirects`. `RedirectSource` preserves a -shell-default stream, an explicit numeric descriptor, or PowerShell's `*` -selector without erasing shell identity. Invalid source-kind/descriptor -combinations are incomplete and fail closed. File targets are path-relevant; -static descriptor duplicate, close, and move operations are not paths. - -`HereDocument` is non-null only for `HereDocument`. A quoted delimiter makes -the body `Literal`; an expanding body is complete only when every supported -execution-bearing substitution has been discovered as its own command -occurrence. Bash `HereString` data uses `Target`, includes the shell's trailing -newline in an exact value, and is not path-relevant. PowerShell here-strings -remain ordinary value tokens rather than redirect operations. +`Authored` references the exact corresponding `Clause.Redirects` leaf. +`RedirectSource` preserves a shell-default stream, an explicit numeric +descriptor, or PowerShell's `*` selector without erasing shell identity. File +alternatives are path-relevant; descriptor alternatives are not paths. + +A quoted heredoc delimiter makes the body `Literal`; an expanding body is +complete only when every supported execution-bearing substitution has been +discovered as its own command occurrence. Bash `HereString` data uses `Data`, +includes the shell's trailing newline in an exact value, and is not +path-relevant. PowerShell here-strings remain ordinary value tokens. + +`RedirectSource.Unknown` is valid only with `UnresolvedRedirectAnalysis`. +`RedirectSource.Default` may own ordinary or combined file, descriptor, +heredoc, or here-string alternatives. +`RedirectSource.Descriptor` may own ordinary file, descriptor, heredoc, or +here-string alternatives but never combined-output. PowerShell all-streams may +own only output/append file alternatives or descriptor duplication to stream +1. Any other internal pairing discards the authorization projection. A Bash file redirect whose expansion cannot prove exactly one target has an `Unknown` target and `IsComplete=false`; its containing command occurrence is diff --git a/docs/CONSUMER_GUIDE.md b/docs/CONSUMER_GUIDE.md index e646206..6666505 100644 --- a/docs/CONSUMER_GUIDE.md +++ b/docs/CONSUMER_GUIDE.md @@ -129,8 +129,8 @@ Once parsed, a security-oriented consumer normally follows this sequence: 2. Walk every command occurrence; do not authorize only the first stage of a compound, pipeline, loop, substitution, or execution region. 3. Determine a conservative command identity. -4. Overlay effective values on authored elements, then evaluate working- - directory and redirect facts. +4. Evaluate the already-joined arguments, working-directory domain, and + redirect alternatives. 5. Elevate dynamic or unresolved content when it affects the policy decision. 6. Apply product-specific rules and produce a decision. @@ -152,31 +152,22 @@ var commandDecision = GateDecision.Allow(); foreach (var occurrence in parsed.Commands) { + var gateKey = GetGateKey(occurrence.Clause.Verb); GateDecision occurrenceDecision; if (!occurrence.IsComplete || !IsKnownRole(occurrence.ImmediateRole) - || occurrence.Clause.Verb.IsDynamic) + || gateKey is null) { occurrenceDecision = GateDecision.Prompt( "command execution is not statically bounded"); } else { - var gateKey = GetGateKey(occurrence.Clause.Verb); - if (gateKey is null) - { - occurrenceDecision = GateDecision.Prompt( - "occurrence has no statically known command"); - } - else - { - // This application-owned step must interpret the authored - // Clause.Elements, overlay EffectiveArguments by - // ClauseElementIndex, and apply the complete grammar for gateKey - // to every exact or finite candidate. - occurrenceDecision = EvaluateOccurrence(gateKey, occurrence); - } + // Arguments already joins each non-cwd Arg to its source element and + // effective value. Apply the complete grammar for gateKey to every + // exact or finite candidate. + occurrenceDecision = EvaluateOccurrence(gateKey, occurrence); } // Do not return early on Prompt: a later occurrence may be Deny. @@ -190,10 +181,8 @@ return commandDecision; static bool IsKnownRole(CommandOccurrenceRole role) => role is CommandOccurrenceRole.Ordinary or CommandOccurrenceRole.PipelineStage - or CommandOccurrenceRole.Condition or CommandOccurrenceRole.Iterator or CommandOccurrenceRole.LoopBody - or CommandOccurrenceRole.Branch or CommandOccurrenceRole.Substitution or CommandOccurrenceRole.ExecutionRegion; ``` @@ -209,29 +198,49 @@ approval must never bypass a deny. ## v0.3 authorization and migration contract -The `0.3.0-alpha.1` package adds `ParsedCommand.Commands` as the authorization -projection and `ParsedCommand.Syntax` as the typed display/analysis tree. This -guide describes the stable v0.3 contract; constructs not yet complete in an -installed prerelease remain prompt-or-deny cases. The migration rules are: +`ParsedCommand.Commands` is the authorization projection and +`ParsedCommand.Syntax` is the typed display/analysis tree. The stable v0.2 +surface remains available: `Source`, `Clauses`, `IsUnparseable`, and +`UnparseableReason` retain their existing contract throughout v0.3. + +The stable v0.3 migration rules are: 1. Check `IsUnparseable` first. An unparseable result has empty `Commands` and `Clauses`; any partial `Syntax` is diagnostic only. -2. Authorize every `CommandOccurrence`, including iterator, condition, branch, - substitution, and loop-body commands. Do not recursively walk `Syntax` to - discover commands. +2. Authorize every `CommandOccurrence`, including iterator, loop-body, + substitution, and execution-region commands. Do not recursively walk + `Syntax` to discover commands. 3. Require `CommandOccurrence.IsComplete`, a recognized `ImmediateRole`, and a static command identity before considering approval reuse. 4. Preserve authored PowerShell parameter/argument classification, then apply shell binding and executable-specific grammar to every exact or finite effective value. A value that begins with `-` can affect a native command; it does not retroactively become a PowerShell cmdlet parameter token. -5. Evaluate every redirect through its explicit operation, source, target, - path relevance, and completeness. Do not infer descriptor safety from raw - prefixes. +5. Evaluate every redirect through its closed runtime alternative, source, + value, and completeness. Do not infer descriptor safety from raw prefixes. 6. Prompt or deny when an unknown value can affect identity, options, path scope, cwd, or redirects. A structurally complete occurrence may still have an unknown value; those are separate facts. +The prerelease `0.3.0-alpha.*` surface was experimental and has no +compatibility promise. Consumers moving from an alpha must make these source +changes: + +| Prerelease shape | Stable v0.3 shape | +|---|---| +| `EffectiveArguments` plus `ClauseElementIndex` | `Arguments`, with direct `Argument` and `Element` references | +| `ShellValueDomain.Kind`, `Values`, `Pattern` | pattern-match `Unknown`, `Exact`, `FiniteSet`, or `PathPattern` | +| `RedirectAnalysis.Operation` property bag | pattern-match the redirect record alternative | +| `RedirectSource.Kind` plus `Descriptor` | pattern-match `Default`, `Descriptor`, `PowerShellAllStreams`, or `Unknown` | +| copied ancestry kind/span fields | `CommandAncestryFrame.Ancestor` plus `Region` and `ChildIndex` | +| `ExecutionRegionSyntax.HostClauseElementIndex` | `HostArgument`, the actual `ClauseElement` | +| `ShellSyntaxKind` | pattern-match the runtime syntax-node type | +| public condition/branch nodes and roles | removed because no parser published them | + +There are intentionally no obsolete aliases or adapters for those alpha +shapes. Recompile against the selected package and fix every use; this avoids +silently preserving an obsolete property-bag policy interpretation. + This authorization is about authored shell syntax. `IsComplete` means the parser found and classified every executable region in the submitted command; it does not promise which runtime executable an ambient alias, function, @@ -339,23 +348,26 @@ release; no removal version is scheduled. A later removal would require a deliberate minor-version breaking change and release-note migration mapping under the repository's `0.x` versioning contract. -The new records participate in generated record equality, hashing, and -`ToString()`. Adding `Syntax` and `Commands` also changes those generated +All new v0.3 result-type constructors and member setters are parser-owned, and +all lists introduced by v0.3 are defensive read-only snapshots. Stable v0.2 +construction and list semantics remain unchanged. The records participate in generated +record equality, hashing, and `ToString()`. Adding `Syntax` and `Commands` also changes those generated results for `ParsedCommand`, even when the compatibility `Clauses` are equal. Do not use a parser result's record hash or `ToString()` as a durable approval key. ShellSyntaxTree does not promise a stable serialized wire format for its closed polymorphic syntax family and does not configure polymorphic JSON serialization. Consumers that persist results should map them to a -consumer-owned, versioned DTO and reject unknown enum values or node kinds -when reading it. +consumer-owned, versioned DTO and reject unknown enum values or runtime +alternatives when reading it. ## Display traversal is not authorization traversal `ParsedCommand.Syntax` preserves authored nesting for explainers, diagnostics, and visualizations. A display can recursively visit `ShellBlockSyntax`, `PipelineSyntax`, `ForEachSyntax`, `CommandSubstitutionSyntax`, -`ExecutionRegionSyntax`, and the other known node types. It must include a -default branch for a node type or `ShellSyntaxKind` added by a future package. +`ExecutionRegionSyntax`, and the other known node types through runtime type +patterns. It must include a default branch for a node type added by a future +package; stable v0.3 deliberately has no redundant public `ShellSyntaxKind`. Do not use that recursive display walk to build an authorization list. The library has already projected every supported executable leaf exactly once @@ -366,37 +378,168 @@ types can omit executable regions. If `IsUnparseable` is true, any partial ## Interpreting occurrence analysis -`EffectiveArguments` overlays bounded runtime values onto authored -`Clause.Elements` by `ClauseElementIndex`; it does not replace the authored -token or its shell classification. Validate each coordinate before use and -apply the executable's complete argument grammar to every candidate: +Each `AnalyzedArgument` directly joins one authored `Arg`, its source +`ClauseElement`, and its effective `ShellValueDomain`. There is exactly one +entry for every non-cwd `Clause.Args` entry, in authored order. Attached forms +such as `--work-tree=../repo` and `-Path:C:\repo` can produce two `Arg` +records that share one source element; consumers do not need to reconstruct +that normal many-to-one relationship from indexes or source spans. + +The examples in this guide use a compact result notation rather than dumping +the complete object graph. Each one shows the submitted input, the +policy-relevant facts returned by the parser, and the decision those facts +enable. Names such as `Exact("/work")` and `Descriptor(2)` denote the +corresponding closed runtime alternatives, not strings that consumers need to +parse. + +For example, parse this with `BashParser`, `WorkingDirectory = "/work"`: + +```bash +cat file.txt | grep x && rm /tmp/stale +``` + +The authorization projection is: + +| `Commands` index | Authored command | `ImmediateRole` | `IsComplete` | `WorkingDirectory` | +|---:|---|---|---|---| +| 0 | `cat file.txt` | `PipelineStage` | `true` | `Exact("/work")` | +| 1 | `grep x` | `PipelineStage` | `true` | `Exact("/work")` | +| 2 | `rm /tmp/stale` | `Ordinary` | `true` | `Exact("/work")` | + +The consumer evaluates all three rows. It may group the first two into one +pipeline-shaped prompt for display, but that grouping does not authorize the +second stage implicitly. The final `rm` occurrence is also evaluated even if +an earlier occurrence already requires a prompt, because it may produce a +hard deny. + +Attached option forms demonstrate why `AnalyzedArgument` includes direct +object references. For this Bash input: + +```bash +git --work-tree=../repo status +``` + +`Commands[0].Arguments` contains three entries: + +| `Argument.Raw` | `Value` | `Element.Raw` | +|---|---|---| +| `--work-tree` | `Exact("--work-tree")` | `--work-tree=../repo` | +| `../repo` | `Exact("../repo")` | `--work-tree=../repo` | +| `status` | `Exact("status")` | `status` | + +The first two entries reference the same `ClauseElement`. A consumer can bind +the option and its operand without source-span arithmetic or re-tokenizing the +command. PowerShell attached parameters such as +`Remove-Item -Path:C:\repo` use the same many-to-one shape. + +Apply the executable's complete argument grammar to every value: + +```csharp +foreach (var analyzed in occurrence.Arguments) +{ + var current = analyzed.Value switch + { + ShellValueDomain.Exact exact => + EvaluateArgument(analyzed.Argument, analyzed.Element, exact.Value), + ShellValueDomain.FiniteSet finite => + EvaluateEveryCandidate( + analyzed.Argument, + analyzed.Element, + finite.Values), + ShellValueDomain.PathPattern pattern => + EvaluatePattern(pattern.Pattern, pattern.CoveringDirectory), + ShellValueDomain.Unknown => + GateDecision.Prompt("policy-sensitive argument is unknown"), + _ => GateDecision.Prompt("unrecognized value-domain alternative"), + }; + + decision = MostRestrictive(decision, current); +} +``` - `Exact` contains one proved value. - `FiniteSet` contains 2 through 32 distinct proved values. Every candidate must independently satisfy policy; do not authorize only the first. -- `Pattern` is a Bash path-shaped glob plus a conservative +- `PathPattern` is a Bash path-shaped glob plus a conservative `CoveringDirectory`. Accept it only when policy understands both the pattern and the full covering scope without enumerating the filesystem. - `Unknown` is not an empty string or wildcard grant. Prompt or deny whenever the value can affect identity, option binding, a path, or another policy-sensitive position. -Only the combinations documented above are valid. An empty `Exact`, a -one-value `FiniteSet`, populated `Values` on `Unknown`, or an unrecognized -`ShellValueDomainKind` is invalid external data and must fail closed. +Use runtime type patterns rather than a parallel kind enum. Keep a default +prompt-or-deny branch so a future library-owned alternative cannot be treated +as safe accidentally. `WorkingDirectory` uses the same domain type, but stable v0.3 publishes only `Exact` or `Unknown`. `Exact` means all modeled reachable states agree. A -branch, loop, failed location change, or unmodeled mutation whose exits do not -agree produces `Unknown`; never substitute the process cwd as a fallback. +branch, zero-or-more loop, failed location change, or unmodeled mutation whose +exits do not agree produces `Unknown`; never substitute the process cwd as a +fallback. + +Redirect analysis is independent. Require `RedirectAnalysis.IsComplete` and +pattern-match its closed runtime alternative. File alternatives carry path +domains; descriptor alternatives are not paths; heredoc and here-string +alternatives carry stdin data whose meaning remains receiver-specific. An +occurrence can be complete while an argument, cwd, or redirect value is +unknown, so test all facts separately. + +## Evaluating loops + +A loop body is represented once as authored syntax. ShellSyntaxTree does not +pretend that it executed the loop or duplicate a command occurrence for every +candidate value. Instead, it gives the loop-dependent argument a value domain. + +With `BashInitialStateMode.IsolatedNonInteractive` and +`WorkingDirectory = "/work"`, this input: + +```bash +for f in a.txt b.txt; do rm -- "$f"; done +``` + +produces one loop-body occurrence: + +```text +Commands[0] + Clause.Verb.Tokens: ["rm"] + ImmediateRole: LoopBody + IsComplete: true + WorkingDirectory: Exact("/work") + Arguments[0]: "--" -> Exact("--") + Arguments[1]: "\"$f\"" -> FiniteSet("a.txt", "b.txt") +``` + +The consumer applies the complete `rm` grammar and path policy to both +`a.txt` and `b.txt`. It must not approve only the first candidate, and it must +not mistake one occurrence for proof that the command runs only once. + +PowerShell uses the same consumer shape. Under +`PwshInitialStateMode.IsolatedNonInteractiveNoProfile`, this input: -Redirect analysis is independent. Require `RedirectAnalysis.IsComplete`, a -recognized source and operation, valid descriptor combinations, and a value -domain appropriate to the operation. Evaluate every path-relevant target. -Descriptor operations are not paths, while heredoc and here-string targets are -stdin data; executable-specific policy still decides whether that data is -code. An occurrence can be structurally complete while one argument, cwd, or -redirect target remains unknown, so test all of these facts separately. +```powershell +foreach ($f in @('a.txt', 'b.txt', 'a.txt')) { Write-Output $F } +``` + +produces one `LoopBody` occurrence whose `$F` argument is +`FiniteSet("a.txt", "b.txt")`; PowerShell's case-insensitive variable binding +and duplicate elimination have already been reflected in the domain. + +The isolated modes are executor assertions, not parser optimizations. With +the safe default initial-state modes, these ambient-variable-dependent proofs +remain unknown or make the construct unparseable as specified earlier. A +consumer must not select an isolated mode merely to obtain a finite set. + +Loops also affect later state even when their body facts are static. With an +incoming cwd of `/work`: + +```bash +for f in /tmp/*.txt; do cd /tmp; done; pwd +``` + +the loop may execute zero times, so both the body `cd` occurrence and the +later `pwd` occurrence report `WorkingDirectory = Unknown`. The reachable +states are `/work` and `/tmp`; the parser does not choose whichever value +would make policy easiest. A cwd-sensitive consumer prompts or denies. ## Choosing a command identity @@ -523,6 +666,19 @@ For `cd /repo && cat file.txt`, the `cat` clause receives a synthetic directory. PowerShell provides the same contract for `Set-Location` and its aliases. +With an incoming cwd of `/work`, the relevant output is: + +| Occurrence | `WorkingDirectory` | Authored path | `Arg.Resolved` | +|---|---|---|---| +| `cd /repo` | `Exact("/work")` | `/repo` | `/repo` | +| `cat file.txt` | `Exact("/repo")` | `file.txt` | `/repo/file.txt` | + +The `cd` row reports the directory in which `cd` itself runs; the `cat` row +reports the successful `AndIf` continuation state. This is why consumers +should use the occurrence's `WorkingDirectory` for execution context and the +argument's `Resolved` value for path-zone policy rather than trying to infer +either from clause order. + The attributed argument is derived context: - use it when evaluating where a clause operates; @@ -543,6 +699,18 @@ write outside an allowed zone: echo safe > /etc/profile.d/example.sh ``` +With a Bash working directory of `/work`, representative results are: + +| Input | Redirect alternative | Source | Relevant value | Complete? | Consumer consequence | +|---|---|---|---|---:|---| +| `echo safe > /etc/profile.d/example.sh` | `FileRedirectAnalysis` with `Mode = Output` | `Default` | `Target = Exact("/etc/profile.d/example.sh")` | yes | Apply write-path policy to the exact target. | +| `command 2>&1` | `DescriptorDuplicateRedirectAnalysis` | `Descriptor(2)` | `TargetDescriptor = 1` | yes | Apply descriptor policy; do not treat `1` as a path. | +| `command 2>&$FD` | `UnresolvedRedirectAnalysis` | `Unknown` | no proved target descriptor | no | Prompt or deny the occurrence. | + +Those are runtime alternatives, not interpretations of a string prefix. In +particular, the incomplete third row cannot accidentally pass a rule written +for ordinary stderr-to-stdout duplication. + For a v0.2 compatibility consumer, walk `Clause.Redirects` independently of `Args`: @@ -571,69 +739,46 @@ var redirectDecision = GateDecision.Allow(); foreach (var redirect in occurrence.Redirects) { - GateDecision current; - - if (!redirect.IsComplete - || !IsKnownRedirectSource(redirect.Source) - || !IsKnownRedirectOperation(redirect.Operation)) - { - current = GateDecision.Prompt("redirect analysis is incomplete"); - } - else if (!redirect.IsPathRelevant) - { - if (redirect.Operation is RedirectOperation.HereDocument - or RedirectOperation.HereString) - { - // Preserve Target and HereDocument for receiver-specific stdin - // policy; this data may be code for the receiving executable. - current = EvaluateStdinData(occurrence, redirect); - } - else - { - current = EvaluateDescriptorOperation( - redirect.Source, - redirect.Operation, - redirect.TargetDescriptor); - } - } - else if (redirect.Target.Kind is not ( - ShellValueDomainKind.Exact or ShellValueDomainKind.FiniteSet)) - { - current = GateDecision.Prompt("redirect path is unknown"); - } - else - { - current = GateDecision.Allow(); - foreach (var path in redirect.Target.Values) + var current = !redirect.IsComplete || !IsKnownSource(redirect.Source) + ? GateDecision.Prompt("redirect analysis is incomplete") + : redirect switch { - current = MostRestrictive(current, EvaluatePath(path)); - } - } + FileRedirectAnalysis file => EvaluateFileRedirect(file), + DescriptorDuplicateRedirectAnalysis duplicate => + EvaluateDescriptorDuplicate(redirect.Source, duplicate.TargetDescriptor), + DescriptorMoveRedirectAnalysis move => + EvaluateDescriptorMove(redirect.Source, move.TargetDescriptor), + DescriptorCloseRedirectAnalysis => + EvaluateDescriptorClose(redirect.Source), + HereDocumentRedirectAnalysis heredoc => + EvaluateStdinData(occurrence, heredoc.Document), + HereStringRedirectAnalysis hereString => + EvaluateStdinData(occurrence, hereString.Data), + UnresolvedRedirectAnalysis => + GateDecision.Prompt("redirect operation is unresolved"), + _ => GateDecision.Prompt("unrecognized redirect alternative"), + }; redirectDecision = MostRestrictive(redirectDecision, current); } return redirectDecision; -static bool IsKnownRedirectSource(RedirectSource source) => source.Kind switch -{ - RedirectSourceKind.Default => source.Descriptor is null, - RedirectSourceKind.Descriptor => source.Descriptor >= 0, - RedirectSourceKind.PowerShellAllStreams => source.Descriptor is null, - _ => false, -}; - -static bool IsKnownRedirectOperation(RedirectOperation operation) => - operation is RedirectOperation.FileInput - or RedirectOperation.FileOutput - or RedirectOperation.FileAppend - or RedirectOperation.DescriptorDuplicate - or RedirectOperation.DescriptorClose - or RedirectOperation.DescriptorMove - or RedirectOperation.CombinedOutput - or RedirectOperation.CombinedOutputAppend - or RedirectOperation.HereDocument - or RedirectOperation.HereString; +static bool IsKnownSource(RedirectSource source) => source is + RedirectSource.Default + or RedirectSource.Descriptor + or RedirectSource.PowerShellAllStreams; + +static GateDecision EvaluateFileRedirect(FileRedirectAnalysis redirect) => + redirect.Target switch + { + ShellValueDomain.Exact exact => EvaluatePath(exact.Value), + ShellValueDomain.FiniteSet finite => EvaluateEveryPath(finite.Values), + ShellValueDomain.PathPattern pattern => + EvaluatePattern(pattern.Pattern, pattern.CoveringDirectory), + ShellValueDomain.Unknown => GateDecision.Prompt("redirect path is unknown"), + _ => GateDecision.Prompt("unrecognized redirect target alternative"), + }; ``` Completeness and value precision are intentionally independent. For example, @@ -642,7 +787,7 @@ target, so path policy still prompts. Under an isolated fresh-process PowerShell initial state, `foreach ($f in @('one.txt','two.txt')) { Write-Output x > $f }` can instead expose a finite set of two absolute target -paths. The loop target is not added to `EffectiveArguments`, because a +paths. The loop target is not added to `Arguments`, because a redirect operand is not part of the command's argv. PowerShell stream facts retain numbered sources and the all-streams selector: @@ -698,6 +843,24 @@ invocation. By contrast, `& $(Write-Output Get-Date)` also retains an incomplete dynamic outer occurrence because PowerShell invokes the produced name. +Bash exposes the same execution-before-container ordering. For: + +```bash +rm "$(find /tmp)" +``` + +the relevant projection is: + +| `Commands` index | Command | `ImmediateRole` | Argument value | +|---:|---|---|---| +| 0 | `find /tmp` | `Substitution` | `/tmp` is `Exact("/tmp")` | +| 1 | `rm "$(find /tmp)"` | `Ordinary` | produced filename is `Unknown` | + +The `find` occurrence is independently authorizable, but its presence does not +make the bytes it prints a statically known `rm` operand. A path-sensitive +policy therefore evaluates `find` and still prompts or denies `rm`. It does not +walk `Syntax` afterward and authorize `find` a second time. + Quoting also determines the scope of host-wrapper substitutions. In `pwsh -Command "Write-Output $(Get-Date)"`, the parent evaluates `Get-Date`, so the result contains that parent-scope occurrence plus an incomplete outer @@ -734,6 +897,18 @@ The recoverable outcome is normally a user prompt with a one-time option, or a deny. A false-negative approval match causes another prompt; a false-positive match can silently execute something the operator did not authorize. +Two different result shapes reach that same safe outcome: + +| Input and parser | Relevant output | Why reusable approval stops | +|---|---|---| +| PowerShell: `& $exe` | `IsUnparseable = false`; one occurrence with `IsComplete = false` and `Verb.IsDynamic = true` | The syntax is recognized, but the executable identity is not bounded. | +| Bash: `if true; then echo ok; fi` | `IsUnparseable = true`; `Commands` and `Clauses` are empty | The unsupported control construct may contain execution, so partial syntax is diagnostic only. | + +`IsUnparseable = false` is therefore not an allow signal. It means only that +the whole input was not rejected as an unsupported or unsafe-to-project +construct; the consumer still checks every occurrence and every +policy-sensitive domain. + ## Worked use cases ### AI-agent approval gate diff --git a/openspec/changes/v0-3-structured-shell-analysis/design.md b/openspec/changes/v0-3-structured-shell-analysis/design.md index 94bede1..70be56a 100644 --- a/openspec/changes/v0-3-structured-shell-analysis/design.md +++ b/openspec/changes/v0-3-structured-shell-analysis/design.md @@ -1123,9 +1123,11 @@ constructed by the library and exposes read-only facts to consumers. Internal constructors and `internal init` accessors are shown to make ownership explicit but are not consumer-callable API. -Every exposed `IReadOnlyList` is backed by a library-owned immutable -collection or a defensive copy. The parser never publishes a mutable array or -list that a consumer can cast and change after projection. +Every `IReadOnlyList` introduced by v0.3 is backed by a library-owned +immutable collection or a defensive copy. The parser never publishes a mutable +array or list for a new v0.3 member that a consumer can cast and change after +projection. Stable v0.2 construction, setters, and list semantics are +unchanged. Runtime type is the discriminant for each closed family. Consumers use an exhaustive type switch with a default fail-closed arm. The library may add a diff --git a/openspec/changes/v0-3-structured-shell-analysis/tasks.md b/openspec/changes/v0-3-structured-shell-analysis/tasks.md index 67bad72..9354f9c 100644 --- a/openspec/changes/v0-3-structured-shell-analysis/tasks.md +++ b/openspec/changes/v0-3-structured-shell-analysis/tasks.md @@ -29,7 +29,7 @@ `PwshDialect` contract. Preserve PowerShell 7 as the compatibility default, make Windows PowerShell 5.1 explicit, keep Bash and PowerShell top-level parsing separate, and require dialect-local grammar/catalog proof. -- [ ] 1.14 Correct the unreleased v0.3 consumer API before stable release. +- [x] 1.14 Correct the unreleased v0.3 consumer API before stable release. Preserve every stable v0.2 type and the `Syntax` / `Commands` / `Clauses` lanes, but do not preserve alpha-only v0.3 members. Replace sparse effective- argument coordinates with one joined analyzed argument per authored non-cwd @@ -37,11 +37,12 @@ closed parser-owned alternatives; reference actual syntax ancestors; remove redundant kind/source copies and public condition/branch types that stable v0.3 never emits; keep executable policy consumer-owned. -- [ ] 1.15 Synchronize the corrected API into `SPEC.md`, implementation, +- [x] 1.15 Synchronize the corrected API into `SPEC.md`, implementation, public-API snapshots, corpus DTOs, README, and consumer guide in one vertical slice. Add prerelease migration notes for alpha consumers without an adapter or obsolete compatibility layer. Defensively copy or immutably back every - published `IReadOnlyList` and pin many-to-one inline-option argument joins. + `IReadOnlyList` introduced by v0.3 and pin many-to-one inline-option + argument joins without changing stable v0.2 list semantics. - [ ] 1.16 Migrate Netclaw from alpha.5 to the corrected prerelease, delete its coordinate/property-bag validation, and rerun the full Linux plus native- Windows approval matrices before stable v0.3. @@ -71,7 +72,7 @@ - [x] 3.10b Implement the bounded expanding-heredoc slice with quote-removed delimiters, literal quoted/escaped bodies, tab stripping, exact provenance, and fail-closed unsupported header/body forms. - [x] 3.10c Require proved Bash variable-attribute state for simple named-parameter dereferences and fail closed globally on the locked unmodeled execution-bearing builtin catalog, including exact dispatch-wrapper bypasses. - [x] 3.10d Fail closed globally on Bash `exec`, mutating or ambiguous `hash`, alias, `shopt`, and `enable` forms, and unmodeled reserved execution prefixes/groups while retaining only exact static query grammar. -- [ ] 3.11 Implement PowerShell `$()` discovery in supported words, redirect values, foreach expressions, call-operator dynamic identities, standalone expression statements, double-quoted strings, and expandable here-strings; never invent invocation from standalone output, retain literal/escaped spellings, and fail closed on trailing command-style arguments, call-operator script blocks, or unsupported execution-bearing `@()` / `@{}` forms. +- [x] 3.11 Implement PowerShell `$()` discovery in supported words, redirect values, foreach expressions, call-operator dynamic identities, standalone expression statements, double-quoted strings, and expandable here-strings; never invent invocation from standalone output, retain literal/escaped spellings, and fail closed on trailing command-style arguments, call-operator script blocks, or unsupported execution-bearing `@()` / `@{}` forms. - [x] 3.11a Implement words, redirect values, call-operator dynamic identities, standalone statements, expandable strings/here-strings, and parent-versus-child host payload provenance; fail closed on arbitrary expression values and unsupported execution-bearing `@()` / `@{}` forms. - [x] 3.12 Pin substitution parentage, authored sibling indices, innermost-first ordering, Bash-isolated versus PowerShell-current-scope state, unknown-state propagation, nesting/depth limits, and incomplete dynamic identities in direct tests. - [x] 3.12a Pin the Bash argument/redirect slice, isolated cwd behavior, wrapper provenance, and the shared structural-depth budget. @@ -83,8 +84,8 @@ - [x] 3.13a Promote the Bash ordinary, multiple, nested, redirect, quoted, escaped, stateful, malformed, and hidden-execution cases into its executable corpus. - [x] 3.13b Promote the PowerShell ordinary, multiple, nested, redirect, quoted, escaped, stateful, malformed, expression-boundary, and hidden-execution cases into its executable corpus. - [x] 3.13c Promote expanding, literal, tab-stripped, multiple, and malformed Bash heredoc cases with full structural expectations into the executable corpus. - - [ ] 3.13d Promote sanitized nameref, unknown-state dereference, and execution-bearing builtin failures into the Bash executable corpus and Netclaw strict matrix. - - [ ] 3.13e Promote sanitized Bash hash, alias/option, exec, builtin-enable, and reserved execution syntax failures into the executable corpus and Netclaw strict matrix. + - [x] 3.13d Promote sanitized nameref, unknown-state dereference, and execution-bearing builtin failures into the Bash executable corpus and Netclaw strict matrix. + - [x] 3.13e Promote sanitized Bash hash, alias/option, exec, builtin-enable, and reserved execution syntax failures into the executable corpus and Netclaw strict matrix. - [x] 3.14 Add `ExecutionRegionSyntax`, its four discriminant enums, `SimpleCommandSyntax.ExecutionRegions`, and appended occurrence/ancestry enum members to the public API and snapshot without changing existing enum values. @@ -146,7 +147,7 @@ - [x] 6.2 Emit condition-free loop-body occurrences and conservative compatibility clauses. - [x] 6.3 Derive exact and finite literal binding domains within the locked candidate cap. - [x] 6.4 Substitute a bounded binding only where Bash quoting proves argument boundaries. -- [ ] 6.5 Propagate and conservatively join cwd and supported binding state across zero-or-more loop execution. +- [x] 6.5 Propagate and conservatively join cwd and supported binding state across zero-or-more loop execution. - [x] 6.5a Lock outcome-partitioned Bash flow, failure-aware `cd`, conservative `lastpipe` / `pipefail`, ordered iteration plans, decoded-wrapper inheritance, and dynamic fail-closed compatibility @@ -154,7 +155,7 @@ - [x] 6.5b Apply outcome-sensitive cwd analysis to existing Bash lists, pipelines, substitutions, subshells, and decoded wrappers; rebase exact compatibility paths and retain `` after conservative joins. - - [ ] 6.5c Carry ordered loop binding and cwd state through zero-or-more + - [x] 6.5c Carry ordered loop binding and cwd state through zero-or-more iterations, then remove the temporary loop-mutation rejection. - [x] 6.5c.1 Correct the contract after adversarial review: require an explicit isolated initial-state mode and supported scalar-name boundary; @@ -187,7 +188,11 @@ - The executable corpus includes indirect and parameter-operator rejection, loop-body substitution, and atomic transition-budget overflow; native oracles pin the shell semantics behind the conservative boundaries. -- [ ] 6.8 Add sanitized Bash corpus entries and Netclaw allow/prompt/deny integration cases. +- [ ] 6.8 Add sanitized Bash corpus entries and Netclaw disposition cases. + Under Netclaw's enforced `BashInitialStateMode.Unknown` contract, bounded + loop cases must remain prompt or deny; require an allow case only if the + executor later proves the full isolated non-interactive startup/environment + contract rather than selecting that parser mode solely to reduce prompts. ## 7. PowerShell Foreach Vertical Slice @@ -255,13 +260,13 @@ sanitizes unknown joins. Broader wrapper, pipeline, alias/cmdlet/native, redirect, and adversarial matrices remain in tasks 7.5-7.7. -- [ ] 7.5 Cover aliases, cmdlets, native commands, nested loops, pipelines, script blocks, and wrapper boundaries. +- [x] 7.5 Cover aliases, cmdlets, native commands, nested loops, pipelines, script blocks, and wrapper boundaries. - [x] 7.5a Implement the version-pinned PowerShell 7 script-block receiver and parameter-binding catalog, including aliases, supported module-qualified identities, parameter abbreviations/inline values, positional binding, parameter sets, `ScriptBlock[]`, and ForEach-Object Begin/Process/End assignment. - - [ ] 7.5b Implement direct `& {}` and `. {}` plus synchronous current-runspace + - [x] 7.5b Implement direct `& {}` and `. {}` plus synchronous current-runspace regions for ForEach-Object, Where-Object, Measure-Command, Trace-Command, in-process Invoke-Command, and New-Module with shell-specific state flow. - [x] 7.5c Implement Start-Job and initialization, ForEach-Object -Parallel, @@ -283,7 +288,7 @@ proved data, an unknown receiver, proved local `Invoke-Command`, the exact module-qualified-looking mutation boundary, and canonical-target invalidation through `echo`. - - [ ] 7.5f Pin authored projection order separately from semantic phase order, + - [x] 7.5f Pin authored projection order separately from semantic phase order, exact host element coordinates, nested regions, wrappers, pipelines, loops, and the 16-container depth boundary. - [x] 7.5g Retain explicit atomic-failure behavior for direct-block arguments @@ -327,7 +332,7 @@ passed on Ubuntu and Windows. Both jobs used the hash-pinned PowerShell 7.6.4 oracle; the Windows job additionally discovered native Windows PowerShell 5.1 from Bash and passed all 2,815 tests plus package creation. -- [ ] 7.10 Migrate Netclaw's native Windows environment to prefer a compatible +- [x] 7.10 Migrate Netclaw's native Windows environment to prefer a compatible `pwsh.exe`, fall back to `powershell.exe`, and carry one canonical platform, executable, and dialect identity through LLM context, parser, approval policy, and executor. Reparse and reauthorize if fallback selection changes. @@ -348,23 +353,26 @@ ## 11. Verification and Release -- [ ] 11.1 Replace the alpha public-API default-value, equality, serialization, +- [x] 11.1 Replace the alpha public-API default-value, equality, serialization, immutability, and unknown-case tests with the corrected stable-v0.3 contract. - - Alpha evidence: `V03PublicApiSnapshotTests` currently pins every additive record default and enum - zero value, proves `Syntax` and `Commands` participate in generated record - equality and `ToString()` plus equal-record hash consistency, demonstrates - that default JSON is not a polymorphic round-trip contract, and makes every - policy-sensitive unknown numeric enum value detectable for consumer rejection. -- [ ] 11.2 Assert every supported executable region appears exactly once and every unsupported executable region makes the result unparseable. -- [ ] 11.3 Run the complete Bash and PowerShell corpus suites plus the PII audit against the corrected implementation. - - Alpha evidence: the complete suites and PII audit pass on the superseded alpha surface. -- [ ] 11.4 Run `dotnet build -c Release`, `dotnet test -c Release`, `dotnet pack -c Release`, and header verification against the corrected implementation. - - Alpha evidence: these gates pass on the superseded alpha implementation. -- [ ] 11.5 Validate the corrected public API field-for-field against the synchronized shared and PowerShell specifications. - - Alpha evidence: `PublicApiSnapshotTests` and `V03PublicApiSnapshotTests` currently enumerate the exact - exported namespace, type family, exact property sets, parser constructors - and entry points, enum ordering, reference nullability, defaults, and fixed - limits synchronized into `SPEC.md` and `SPEC.POWERSHELL.md`. + - Corrected evidence: `V03PublicApiSnapshotTests` pins the closed record + families, parser-owned construction, defensive lists, generated + `ParsedCommand` equality/hash/`ToString()` behavior, absence of a default + polymorphic JSON round trip, and detectable future enum values. +- [x] 11.2 Assert every supported executable region appears exactly once and every unsupported executable region makes the result unparseable. +- [x] 11.3 Run the complete Bash and PowerShell corpus suites plus the PII audit against the corrected implementation. + - Corrected evidence: the complete Release suite passes 2,870/2,870 and the + explicit PII audit passes 1/1, including Bash corpus case 309. +- [x] 11.4 Run `dotnet build -c Release`, `dotnet test -c Release`, `dotnet pack -c Release`, and header verification against the corrected implementation. + - Corrected evidence: Release build passes with zero warnings, all 2,870 + tests pass, package and symbols pack successfully, and header verification + reports every file compliant. +- [x] 11.5 Validate the corrected public API field-for-field against the synchronized shared and PowerShell specifications. + - Corrected evidence: `PublicApiSnapshotTests` and + `V03PublicApiSnapshotTests` enumerate the exact exported namespace, stable + v0.2 members, corrected v0.3 type families, property accessors, parser + entry points, enum ordering, and nullability synchronized into `SPEC.md` + and `SPEC.POWERSHELL.md`. - [ ] 11.6 Validate Netclaw's ordinary-command, redirect, bounded-loop, and unknown-value approval matrices against the prerelease package. - [x] 11.7 Update release notes and remove Netclaw's temporary descriptor workaround only after explicit redirect integration is live. - The `0.3.0-alpha` release notes document the explicit redirect model. The diff --git a/src/ShellSyntaxTree/CommandOccurrence.cs b/src/ShellSyntaxTree/CommandOccurrence.cs index 9853123..d07bed6 100644 --- a/src/ShellSyntaxTree/CommandOccurrence.cs +++ b/src/ShellSyntaxTree/CommandOccurrence.cs @@ -5,147 +5,279 @@ // ----------------------------------------------------------------------- using System; using System.Collections.Generic; +using ShellSyntaxTree.Internal; namespace ShellSyntaxTree; /// One authored simple command that may execute. public sealed record CommandOccurrence { + private IReadOnlyList _ancestry = + Array.Empty(); + private IReadOnlyList _arguments = Array.Empty(); + private IReadOnlyList _redirects = Array.Empty(); + + internal CommandOccurrence() + { + } + /// Gets the shared compatibility leaf. - public Clause Clause { get; init; } = new(); + public Clause Clause { get; internal init; } = new(); /// Gets the nearest structural execution role. - public CommandOccurrenceRole ImmediateRole { get; init; } + public CommandOccurrenceRole ImmediateRole { get; internal init; } /// Gets ancestry ordered from outermost to innermost. - public IReadOnlyList Ancestry { get; init; } = - Array.Empty(); + public IReadOnlyList Ancestry + { + get => _ancestry; + internal init => _ancestry = PublicCollection.Copy(value); + } + + /// Gets one analyzed value for every authored, non-cwd argument. + public IReadOnlyList Arguments + { + get => _arguments; + internal init => _arguments = PublicCollection.Copy(value); + } + + internal IReadOnlyList EffectiveArguments + { + get + { + var values = new List(); + for (var index = 0; index < _arguments.Count; index++) + { + if (!_arguments[index].HasEffectiveValue) + { + continue; + } + + values.Add(new EffectiveArgument + { + ClauseElementIndex = FindElementIndex(_arguments[index].Element), + Value = _arguments[index].Value, + }); + } - /// Gets bounded effective values at authored argument coordinates. - public IReadOnlyList EffectiveArguments { get; init; } = - Array.Empty(); + return values.ToArray(); + } + } /// Gets the effective working-directory proof. - public ShellValueDomain WorkingDirectory { get; init; } = ShellValueDomain.Unknown; + public ShellValueDomain WorkingDirectory { get; internal init; } = + new ShellValueDomain.Unknown(); /// Gets explicit redirect analysis in compatibility redirect order. - public IReadOnlyList Redirects { get; init; } = - Array.Empty(); - - /// - /// Gets whether command identity, ancestry, and parser-owned shell analysis - /// are structurally complete. - /// - public bool IsComplete { get; init; } + public IReadOnlyList Redirects + { + get => _redirects; + internal init => _redirects = PublicCollection.Copy(value); + } + + /// Gets whether parser-owned shell analysis is structurally complete. + public bool IsComplete { get; internal init; } + + private int FindElementIndex(ClauseElement element) + { + for (var index = 0; index < Clause.Elements.Count; index++) + { + if (ReferenceEquals(Clause.Elements[index], element)) + { + return index; + } + } + + return -1; + } } /// Identifies the nearest structural role of a command occurrence. public enum CommandOccurrenceRole { - /// The role is unknown. Unknown, - /// An ordinary command. Ordinary, - /// A pipeline stage. PipelineStage, - /// A condition command. - Condition, - /// An iterator-producing command. Iterator, - /// A loop-body command. LoopBody, - /// A conditional-branch command. - Branch, - /// A substitution command. Substitution, - /// A command inside an execution-bearing region. ExecutionRegion, } /// One compositional structural ancestor of a command occurrence. public sealed record CommandAncestryFrame { - /// Gets the ancestor's syntax kind. - public ShellSyntaxKind AncestorKind { get; init; } + internal CommandAncestryFrame() + { + } + + /// Gets the actual ancestor node. + public ShellSyntaxNode Ancestor { get; internal init; } = null!; /// Gets the occurrence's region within the ancestor. - public CommandAncestryRegion Region { get; init; } + public CommandAncestryRegion Region { get; internal init; } /// Gets the child index for repeated regions, when applicable. - public int? ChildIndex { get; init; } + public int? ChildIndex { get; internal init; } - /// Gets the ancestor source start, when exact. - public int? SourceStart { get; init; } + internal ShellSyntaxKind AncestorKind => Ancestor.Kind; - /// Gets the ancestor source length, when exact. - public int? SourceLength { get; init; } + internal int? SourceStart => Ancestor.SourceStart; + + internal int? SourceLength => Ancestor.SourceLength; } /// Identifies an occurrence's region within a structural ancestor. public enum CommandAncestryRegion { - /// The region is unknown. Unknown, - /// The root region. Root, - /// An ordinary statement region. Statement, - /// A pipeline-stage region. PipelineStage, - /// A group body. GroupBody, - /// An iterator expression. Iterator, - /// A loop body. LoopBody, - /// A condition region. - Condition, - /// A conditional branch. - Branch, - /// A command substitution. Substitution, - /// An execution-bearing region. ExecutionRegion, } -/// A bounded effective value at one authored clause-element coordinate. -public sealed record EffectiveArgument +/// One parser-owned join between a compatibility argument and its source. +public sealed record AnalyzedArgument +{ + internal AnalyzedArgument() + { + } + + /// Gets the compatibility argument. + public Arg Argument { get; internal init; } = null!; + + /// Gets the authored element that produced the argument. + public ClauseElement Element { get; internal init; } = null!; + + /// Gets the effective shell-value proof. + public ShellValueDomain Value { get; internal init; } = null!; + + internal bool HasEffectiveValue { get; init; } +} + +internal sealed record EffectiveArgument { - /// Gets the index into . - public int ClauseElementIndex { get; init; } = -1; + internal int ClauseElementIndex { get; init; } = -1; - /// Gets the effective value proof. - public ShellValueDomain Value { get; init; } = ShellValueDomain.Unknown; + internal ShellValueDomain Value { get; init; } = null!; } /// A bounded, non-executing proof for a shell value. -public sealed record ShellValueDomain +public abstract record ShellValueDomain { - /// Gets the shared empty unknown-domain value. - public static ShellValueDomain Unknown { get; } = new(); + private protected ShellValueDomain() + { + } + + private protected abstract object LibraryOwnership { get; } + + internal abstract ShellValueDomainKind InternalKind { get; } + + internal ShellValueDomainKind Kind => InternalKind; + + internal virtual IReadOnlyList InternalValues => Array.Empty(); + + internal IReadOnlyList Values => InternalValues; + + internal virtual string? InternalPattern => null; + + internal string? Pattern => InternalPattern; + + internal virtual string? InternalCoveringDirectory => null; + + internal string? CoveringDirectory => InternalCoveringDirectory; - /// Gets the proof kind. - public ShellValueDomainKind Kind { get; init; } + /// No bounded value is proved. + public sealed record Unknown : ShellValueDomain + { + internal Unknown() + { + } + + private protected override object LibraryOwnership => this; + + internal override ShellValueDomainKind InternalKind => ShellValueDomainKind.Unknown; + } + + /// Exactly one value is proved. + public sealed record Exact : ShellValueDomain + { + internal Exact(string value) => Value = value; + + private protected override object LibraryOwnership => this; + + internal override ShellValueDomainKind InternalKind => ShellValueDomainKind.Exact; + + internal override IReadOnlyList InternalValues => new[] { Value }; + + /// Gets the proved value. + public string Value { get; } + } + + /// Two through 32 distinct values are proved. + public sealed record FiniteSet : ShellValueDomain + { + internal FiniteSet(IEnumerable values) => + Values = PublicCollection.Copy(values); + + private protected override object LibraryOwnership => this; + + internal override ShellValueDomainKind InternalKind => + ShellValueDomainKind.FiniteSet; + + internal override IReadOnlyList InternalValues => Values; - /// Gets exact or finite values. - public IReadOnlyList Values { get; init; } = Array.Empty(); + /// Gets the proved finite values. + public new IReadOnlyList Values { get; } + } - /// Gets a bounded symbolic pattern. - public string? Pattern { get; init; } + /// A bounded path pattern and its conservative covering directory. + public sealed record PathPattern : ShellValueDomain + { + internal PathPattern(string pattern, string coveringDirectory) + { + Pattern = pattern; + CoveringDirectory = coveringDirectory; + } - /// Gets the pattern's conservative covering directory. - public string? CoveringDirectory { get; init; } + private protected override object LibraryOwnership => this; + + internal override ShellValueDomainKind InternalKind => ShellValueDomainKind.Pattern; + + internal override string InternalPattern => Pattern; + + internal override string InternalCoveringDirectory => CoveringDirectory; + + /// Gets the symbolic path pattern. + public new string Pattern { get; } + + /// Gets the conservative covering directory. + public new string CoveringDirectory { get; } + } } -/// Identifies the strength and shape of a shell-value proof. -public enum ShellValueDomainKind +internal enum ShellValueDomainKind { - /// No bounded value is proved. Unknown, - /// Exactly one value is proved. Exact, - /// Two through 32 distinct values are proved. FiniteSet, - /// A pattern and conservative covering directory are proved. Pattern, } + +internal static class ShellValueDomains +{ + internal static ShellValueDomain Unknown { get; } = new ShellValueDomain.Unknown(); + + internal static ShellValueDomain Exact(string value) => new ShellValueDomain.Exact(value); + + internal static ShellValueDomain FiniteSet(IEnumerable values) => + new ShellValueDomain.FiniteSet(values); + + internal static ShellValueDomain Pattern(string pattern, string coveringDirectory) => + new ShellValueDomain.PathPattern(pattern, coveringDirectory); +} diff --git a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashAbstractStateAnalyzer.cs b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashAbstractStateAnalyzer.cs index 562d3b8..a06db50 100644 --- a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashAbstractStateAnalyzer.cs +++ b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashAbstractStateAnalyzer.cs @@ -25,7 +25,7 @@ internal sealed class BashAbstractStateAnalyzer private readonly Func _forInPlanFactory; private readonly Dictionary _inputs = new(ClauseReferenceComparer.Instance); - private readonly Dictionary> + private readonly Dictionary> _effectiveArguments = new(ClauseReferenceComparer.Instance); private readonly Dictionary> _cwdResolutionSanitization = new(ClauseReferenceComparer.Instance); @@ -464,7 +464,7 @@ private void RecordEffectiveArguments( return; } - Dictionary? accumulated = null; + Dictionary? accumulated = null; var evaluator = input.Bindings; foreach (var provenance in sourceFacts.ValueProvenance) { @@ -548,7 +548,7 @@ private static bool RequiresIndependentEffectiveValue( private bool TryAnalyzeParserKnownValue( ShellValue value, - out ShellValueDomain domain) + out ShellValueDomainFacts domain) { var homeDirectory = BashResolver.GetHomeDirectory(_options); var composed = new StringBuilder(value.Decoded.Length); @@ -566,7 +566,7 @@ private bool TryAnalyzeParserKnownValue( if (fragment.Kind != ShellValueFragmentKind.Expansion || fragment.Expansion is not ShellExpansionReference expansion) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } @@ -589,7 +589,7 @@ private bool TryAnalyzeParserKnownValue( if (tildeKind == BashTildeExpansionKind.Unknown || homeDirectory.Length == 0) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } @@ -605,14 +605,14 @@ private bool TryAnalyzeParserKnownValue( ((fragment.AllowedTransforms & ShellLexicalTransform.FieldSplit) != 0 && ContainsFieldSplitOrGlobCharacter(homeDirectory))) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } composed.Append(homeDirectory); } - domain = new ShellValueDomain + domain = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { composed.ToString() }, @@ -677,7 +677,7 @@ private void RecordUnvisitedBindingArguments( bindingName)) { GetEffectiveArguments(simple.Clause)[provenance.ClauseElementIndex] = - ShellValueDomain.Unknown; + ShellValueDomainFacts.Unknown; } } @@ -737,14 +737,14 @@ private void RecordUnvisitedBindingArguments( } } - private Dictionary GetEffectiveArguments(Clause clause) + private Dictionary GetEffectiveArguments(Clause clause) { if (_effectiveArguments.TryGetValue(clause, out var accumulated)) { return accumulated; } - accumulated = new Dictionary(); + accumulated = new Dictionary(); _effectiveArguments.Add(clause, accumulated); return accumulated; } @@ -902,7 +902,7 @@ private void BuildEffectiveCwdArguments( if (domain.Kind != ShellValueDomainKind.Exact && TryResolveKnownHomeWord(argumentValues[index], input, out var homeWord)) { - domain = new ShellValueDomain + domain = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { homeWord }, @@ -1396,20 +1396,20 @@ private SimpleCommandSyntax RewriteSimple( }; } - private IReadOnlyList CreateEffectiveArguments(Clause clause) + private IReadOnlyList CreateEffectiveArguments(Clause clause) { if (!_effectiveArguments.TryGetValue(clause, out var accumulated) || accumulated.Count == 0) { - return Array.Empty(); + return Array.Empty(); } var indices = new List(accumulated.Keys); indices.Sort(); - var effective = new EffectiveArgument[indices.Count]; + var effective = new EffectiveArgumentFacts[indices.Count]; for (var index = 0; index < effective.Length; index++) { - effective[index] = new EffectiveArgument + effective[index] = new EffectiveArgumentFacts { ClauseElementIndex = indices[index], Value = accumulated[indices[index]], @@ -1605,8 +1605,8 @@ private IReadOnlyList RewriteCompatibilityRedirects( return redirects; } - private static IReadOnlyList RewriteRedirectFacts( - IReadOnlyList source, + private static IReadOnlyList RewriteRedirectFacts( + IReadOnlyList source, IReadOnlyList provenance, BashLoopBindingContext bindings, Clause clause) @@ -1616,7 +1616,7 @@ private static IReadOnlyList RewriteRedirectFacts( return source; } - var rewritten = new RedirectAnalysis[source.Count]; + var rewritten = new RedirectAnalysisFacts[source.Count]; for (var index = 0; index < rewritten.Length; index++) { var fact = source[index]; @@ -1644,8 +1644,8 @@ private static IReadOnlyList RewriteRedirectFacts( rewritten[index] = fact with { Target = redirect.IsDynamicSkip - ? ShellValueDomain.Unknown - : new ShellValueDomain + ? ShellValueDomainFacts.Unknown + : new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { redirect.Target }, @@ -1657,8 +1657,8 @@ private static IReadOnlyList RewriteRedirectFacts( return rewritten; } - private static ShellValueDomain RewriteHereStringTarget( - RedirectAnalysis fact, + private static ShellValueDomainFacts RewriteHereStringTarget( + RedirectAnalysisFacts fact, IReadOnlyList provenance, BashLoopBindingContext bindings) { @@ -1677,7 +1677,7 @@ private static ShellValueDomain RewriteHereStringTarget( if (domain.Kind is not ( ShellValueDomainKind.Exact or ShellValueDomainKind.FiniteSet)) { - return ShellValueDomain.Unknown; + return ShellValueDomainFacts.Unknown; } var values = new string[domain.Values.Count]; @@ -1686,7 +1686,7 @@ private static ShellValueDomain RewriteHereStringTarget( values[index] = domain.Values[index] + "\n"; } - return new ShellValueDomain + return new ShellValueDomainFacts { Kind = domain.Kind, Values = values, @@ -1696,7 +1696,7 @@ private static ShellValueDomain RewriteHereStringTarget( return fact.Target; } - private static bool AreRedirectsComplete(IReadOnlyList redirects) + private static bool AreRedirectsComplete(IReadOnlyList redirects) { foreach (var redirect in redirects) { @@ -2128,7 +2128,7 @@ internal BashAbstractState( internal BashAbstractState WithBinding( string name, - ShellValueDomain domain) => + ShellValueDomainFacts domain) => new( WorkingDirectory, HasCompatibilityAttribution, @@ -2145,10 +2145,10 @@ internal BashAbstractState WithCwd( internal BashAbstractState WithoutCompatibilityAttribution() => new(WorkingDirectory, WorkingDirectory is null, Bindings); - internal ShellValueDomain ToDomain() => + internal ShellValueDomainFacts ToDomain() => WorkingDirectory is null - ? ShellValueDomain.Unknown - : new ShellValueDomain + ? ShellValueDomainFacts.Unknown + : new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { WorkingDirectory }, diff --git a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashCommandParser.cs b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashCommandParser.cs index 0148f90..1be23d2 100644 --- a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashCommandParser.cs +++ b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashCommandParser.cs @@ -501,11 +501,16 @@ private readonly record struct RedirectTargetProvenanceSet( Clause Clause, IReadOnlyList Provenance); + private readonly record struct RedirectAnalysisSet( + Clause Clause, + IReadOnlyList Redirects); + private readonly record struct BashParseResult( ParsedCommand Command, IReadOnlyList CwdPathDependencySets, IReadOnlyList ValueProvenanceSets, IReadOnlyList RedirectTargetProvenanceSets, + IReadOnlyList RedirectAnalysisSets, IReadOnlyList ForInPlans); private static ClauseResult ParseClauseSegment( diff --git a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashLoopAnalysis.cs b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashLoopAnalysis.cs index e2f6ce5..6fb8311 100644 --- a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashLoopAnalysis.cs +++ b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashLoopAnalysis.cs @@ -38,10 +38,10 @@ internal sealed record BashForInAnalysisPlanReference( BashForInAnalysisPlan Plan); internal sealed record BashIterationPlan( - IReadOnlyList OrderedCandidates, + IReadOnlyList OrderedCandidates, BashIterationCardinality Cardinality, bool RequiresFixedPoint, - ShellValueDomain Summary); + ShellValueDomainFacts Summary); /// /// Preserves bounded loop-variable proofs while the Bash structural parser @@ -55,7 +55,7 @@ internal sealed class BashLoopBindingContext internal BashLoopBindingContext WithBinding( string name, - ShellValueDomain domain) + ShellValueDomainFacts domain) { var clone = Clone(); for (var index = clone._bindings.Count - 1; index >= 0; index--) @@ -116,7 +116,7 @@ internal BashBindingAlternativeResult EnumerateExactAlternatives( { next.Add(state.WithBinding( binding.Name, - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { value }, @@ -218,7 +218,7 @@ internal static BashLoopBindingContext JoinState( joined._bindings.Add(new BindingFrame( name, leftBinding is null || rightBinding is null - ? ShellValueDomain.Unknown + ? ShellValueDomainFacts.Unknown : JoinDomains(leftBinding.Domain, rightBinding.Domain))); } @@ -251,7 +251,7 @@ leftBinding is not null && rightBinding is not null && DomainEquals(leftBinding.Domain, rightBinding.Domain) ? leftBinding.Domain - : ShellValueDomain.Unknown)); + : ShellValueDomainFacts.Unknown)); } return widened; @@ -281,13 +281,13 @@ internal BashIterationPlan AnalyzeIterationPlan( if (words.Count == 0) { return new BashIterationPlan( - Array.Empty(), + Array.Empty(), BashIterationCardinality.Never, RequiresFixedPoint: false, - ShellValueDomain.Unknown); + ShellValueDomainFacts.Unknown); } - var ordered = new List(words.Count); + var ordered = new List(words.Count); var summary = new List(words.Count); var distinct = new HashSet(StringComparer.Ordinal); foreach (var word in words) @@ -296,7 +296,7 @@ internal BashIterationPlan AnalyzeIterationPlan( { return FixedPointPlan( BashIterationCardinality.ZeroOrMore, - ShellValueDomain.Unknown); + ShellValueDomainFacts.Unknown); } if (TryBuildStaticPattern( @@ -308,10 +308,10 @@ internal BashIterationPlan AnalyzeIterationPlan( return FixedPointPlan(BashIterationCardinality.ZeroOrMore, pattern); } - ShellValueDomain domain; + ShellValueDomainFacts domain; if (IsEntirelyLiteral(word.Value)) { - domain = new ShellValueDomain + domain = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { word.Value.Decoded }, @@ -336,7 +336,7 @@ internal BashIterationPlan AnalyzeIterationPlan( { return FixedPointPlan( BashIterationCardinality.OneOrMore, - ShellValueDomain.Unknown); + ShellValueDomainFacts.Unknown); } } @@ -364,15 +364,15 @@ internal static BashForInAnalysisPlan CapturePlan( private static BashIterationPlan FixedPointPlan( BashIterationCardinality cardinality, - ShellValueDomain summary) => new( - Array.Empty(), + ShellValueDomainFacts summary) => new( + Array.Empty(), cardinality, RequiresFixedPoint: true, summary); - internal static ShellValueDomain JoinDomains( - ShellValueDomain left, - ShellValueDomain right) + internal static ShellValueDomainFacts JoinDomains( + ShellValueDomainFacts left, + ShellValueDomainFacts right) { if (DomainEquals(left, right)) { @@ -384,7 +384,7 @@ internal static ShellValueDomain JoinDomains( right.Kind is not ( ShellValueDomainKind.Exact or ShellValueDomainKind.FiniteSet)) { - return ShellValueDomain.Unknown; + return ShellValueDomainFacts.Unknown; } var values = new List(left.Values.Count + right.Values.Count); @@ -403,7 +403,7 @@ right.Kind is not ( { if (distinct.Count > ShellAnalysisLimits.MaxValueCandidates) { - return ShellValueDomain.Unknown; + return ShellValueDomainFacts.Unknown; } values.Add(candidate); @@ -414,8 +414,8 @@ right.Kind is not ( } private static bool DomainEquals( - ShellValueDomain left, - ShellValueDomain right) + ShellValueDomainFacts left, + ShellValueDomainFacts right) { if (left.Kind != right.Kind || !string.Equals(left.Pattern, right.Pattern, StringComparison.Ordinal) || @@ -441,7 +441,7 @@ private static bool DomainEquals( internal bool TryAnalyzeEffectiveValue( ShellValue value, - out ShellValueDomain domain) + out ShellValueDomainFacts domain) { var referenced = new List(); var dependentButUnsupported = false; @@ -478,13 +478,13 @@ internal bool TryAnalyzeEffectiveValue( if (referenced.Count == 0 && !dependentButUnsupported) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } if (dependentButUnsupported || ContainsUnresolvedFragment(value, referenced)) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return true; } @@ -493,7 +493,7 @@ internal bool TryAnalyzeEffectiveValue( { domain = IsOneBindingExpansion(value, referenced[0]) ? referenced[0].Domain - : ShellValueDomain.Unknown; + : ShellValueDomainFacts.Unknown; return true; } @@ -502,7 +502,7 @@ internal bool TryAnalyzeEffectiveValue( if (binding.Domain.Kind is not ( ShellValueDomainKind.Exact or ShellValueDomainKind.FiniteSet)) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return true; } } @@ -518,7 +518,7 @@ internal bool TryAnalyzeEffectiveValue( candidates, distinct)) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return true; } @@ -526,11 +526,11 @@ internal bool TryAnalyzeEffectiveValue( return true; } - internal ShellValueDomain AnalyzeWordForTransfer(ShellValue value) + internal ShellValueDomainFacts AnalyzeWordForTransfer(ShellValue value) { if (IsEntirelyLiteral(value)) { - return new ShellValueDomain + return new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { value.Decoded }, @@ -539,7 +539,7 @@ internal ShellValueDomain AnalyzeWordForTransfer(ShellValue value) return TryAnalyzeEffectiveValue(value, out var domain) ? domain - : ShellValueDomain.Unknown; + : ShellValueDomainFacts.Unknown; } private bool TryComposeCandidates( @@ -751,7 +751,7 @@ private static bool TryBuildStaticPattern( ShellValue value, BashParserOptions options, bool workingDirectoryUnknown, - out ShellValueDomain pattern) + out ShellValueDomainFacts pattern) { var containsGlob = false; foreach (var fragment in value.Fragments) @@ -765,7 +765,7 @@ private static bool TryBuildStaticPattern( fragment.Expansion is null || fragment.Expansion.Value.Kind != ShellExpansionKind.Glob) { - pattern = ShellValueDomain.Unknown; + pattern = ShellValueDomainFacts.Unknown; return false; } @@ -779,7 +779,7 @@ fragment.Expansion is null || ContainsParentTraversal(authored) || HasGlobBearingDotSegment(authored)) { - pattern = ShellValueDomain.Unknown; + pattern = ShellValueDomainFacts.Unknown; return false; } @@ -799,11 +799,11 @@ fragment.Expansion is null || ShellResolutionConsumer.BashArgument); if (resolved.Resolved is null) { - pattern = ShellValueDomain.Unknown; + pattern = ShellValueDomainFacts.Unknown; return false; } - pattern = new ShellValueDomain + pattern = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Pattern, Pattern = authored, @@ -872,20 +872,20 @@ private static bool HasUnmodeledBraceExpansion(BashToken token) => token.Kind == BashTokenKind.Word && (token.Value.IndexOf('{') >= 0 || token.Value.IndexOf('}') >= 0); - private static ShellValueDomain CreateFiniteDomain(IReadOnlyList values) => + private static ShellValueDomainFacts CreateFiniteDomain(IReadOnlyList values) => values.Count switch { - 1 => new ShellValueDomain + 1 => new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { values[0] }, }, - >= 2 and <= 32 => new ShellValueDomain + >= 2 and <= 32 => new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = Copy(values), }, - _ => ShellValueDomain.Unknown, + _ => ShellValueDomainFacts.Unknown, }; private static string[] Copy(IReadOnlyList values) @@ -901,7 +901,7 @@ private static string[] Copy(IReadOnlyList values) private sealed class BindingFrame { - internal BindingFrame(string name, ShellValueDomain domain) + internal BindingFrame(string name, ShellValueDomainFacts domain) { Name = name; Domain = domain; @@ -909,6 +909,6 @@ internal BindingFrame(string name, ShellValueDomain domain) internal string Name { get; } - internal ShellValueDomain Domain { get; } + internal ShellValueDomainFacts Domain { get; } } } diff --git a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashRedirectAnalysis.cs b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashRedirectAnalysis.cs index f5c6f69..413841d 100644 --- a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashRedirectAnalysis.cs +++ b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashRedirectAnalysis.cs @@ -18,23 +18,23 @@ namespace ShellSyntaxTree.Internal.Bash.Parsing; /// internal static class BashRedirectAnalysis { - internal static IReadOnlyList Analyze(Clause clause) + internal static IReadOnlyList Analyze(Clause clause) => AnalyzeCore(clause, source: null, sourceTokens: null); - internal static IReadOnlyList Analyze( + internal static IReadOnlyList Analyze( Clause clause, string source, IReadOnlyList sourceTokens) => AnalyzeCore(clause, source, sourceTokens); - private static IReadOnlyList AnalyzeCore( + private static IReadOnlyList AnalyzeCore( Clause clause, string? source, IReadOnlyList? sourceTokens) { if (clause.Redirects.Count == 0) { - return Array.Empty(); + return Array.Empty(); } var elements = new List(clause.Redirects.Count); @@ -46,7 +46,7 @@ private static IReadOnlyList AnalyzeCore( } } - var result = new RedirectAnalysis[clause.Redirects.Count]; + var result = new RedirectAnalysisFacts[clause.Redirects.Count]; for (var index = 0; index < result.Length; index++) { result[index] = index < elements.Count @@ -62,7 +62,7 @@ private static IReadOnlyList AnalyzeCore( return result; } - private static RedirectAnalysis Analyze( + private static RedirectAnalysisFacts Analyze( int redirectIndex, Redirect compatibility, ClauseElement element, @@ -114,45 +114,41 @@ RedirectOperation.FileOutput or if (operation == RedirectOperation.Unknown) { - return new RedirectAnalysis - { - RedirectIndex = redirectIndex, - Source = redirectSource, - }; + return Incomplete(redirectIndex); } var isComplete = !compatibility.IsDynamicSkip; - return new RedirectAnalysis + return new RedirectAnalysisFacts { RedirectIndex = redirectIndex, Source = redirectSource, Operation = operation, Target = isComplete - ? new ShellValueDomain + ? new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { compatibility.Target }, } - : ShellValueDomain.Unknown, + : ShellValueDomainFacts.Unknown, IsPathRelevant = true, IsComplete = isComplete, }; } - private static RedirectAnalysis AnalyzeHereString( + private static RedirectAnalysisFacts AnalyzeHereString( int redirectIndex, Redirect compatibility, ClauseElement element, - RedirectSource source) + RedirectSourceFacts source) { var target = compatibility.IsDynamicSkip - ? ShellValueDomain.Unknown - : new ShellValueDomain + ? ShellValueDomainFacts.Unknown + : new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { (element.Resolved ?? element.Value) + "\n" }, }; - return new RedirectAnalysis + return new RedirectAnalysisFacts { RedirectIndex = redirectIndex, Source = source, @@ -191,12 +187,12 @@ internal static ShellValue NormalizeHereStringOperand(ShellValue value) return changed ? new ShellValue(value.Decoded, fragments) : value; } - private static RedirectAnalysis AnalyzeHereDocument( + private static RedirectAnalysisFacts AnalyzeHereDocument( int redirectIndex, string? source, IReadOnlyList? sourceTokens, ClauseElement element, - RedirectSource redirectSource, + RedirectSourceFacts redirectSource, bool stripLeadingTabs) { if (source is null || sourceTokens is null || @@ -246,7 +242,7 @@ token.HeredocBodyLength is null || StripLeadingTabs = stripLeadingTabs, IsComplete = true, }; - return new RedirectAnalysis + return new RedirectAnalysisFacts { RedirectIndex = redirectIndex, Source = redirectSource, @@ -259,9 +255,9 @@ token.HeredocBodyLength is null || return Incomplete(redirectIndex); } - private static RedirectAnalysis AnalyzeDescriptorTarget( + private static RedirectAnalysisFacts AnalyzeDescriptorTarget( int redirectIndex, - RedirectSource source, + RedirectSourceFacts source, string authoredTarget, string decodedTarget) { @@ -269,7 +265,7 @@ private static RedirectAnalysis AnalyzeDescriptorTarget( { if (string.Equals(authoredTarget, "&-", StringComparison.Ordinal)) { - return new RedirectAnalysis + return new RedirectAnalysisFacts { RedirectIndex = redirectIndex, Source = source, @@ -292,7 +288,7 @@ private static RedirectAnalysis AnalyzeDescriptorTarget( out var descriptor) && descriptor >= 0) { - return new RedirectAnalysis + return new RedirectAnalysisFacts { RedirectIndex = redirectIndex, Source = source, @@ -305,21 +301,18 @@ private static RedirectAnalysis AnalyzeDescriptorTarget( } } - return new RedirectAnalysis - { - RedirectIndex = redirectIndex, - Source = source, - Operation = RedirectOperation.DescriptorDuplicate, - }; + // A computed target does not fit any closed descriptor alternative: + // duplicate and move records require a proved target descriptor. + return Incomplete(redirectIndex); } private static bool TryReadOperator( string raw, - out RedirectSource source, + out RedirectSourceFacts source, out RedirectOperation operation, out int length) { - source = new RedirectSource { Kind = RedirectSourceKind.Default }; + source = new RedirectSourceFacts { Kind = RedirectSourceKind.Default }; operation = RedirectOperation.Unknown; length = 0; @@ -366,7 +359,7 @@ private static bool TryReadOperator( out var descriptor) && descriptor >= 0) { - source = new RedirectSource + source = new RedirectSourceFacts { Kind = RedirectSourceKind.Descriptor, Descriptor = descriptor, @@ -374,7 +367,7 @@ private static bool TryReadOperator( } else { - source = new RedirectSource(); + source = new RedirectSourceFacts(); if (raw.AsSpan(operatorStart).StartsWith("<<<", StringComparison.Ordinal)) { operation = RedirectOperation.HereString; @@ -437,7 +430,7 @@ private static bool TryReadOperator( return true; } - source = new RedirectSource(); + source = new RedirectSourceFacts(); return false; } @@ -460,7 +453,7 @@ private static bool TrySkipLineContinuation( return true; } - private static RedirectAnalysis Incomplete(int redirectIndex) => new() + private static RedirectAnalysisFacts Incomplete(int redirectIndex) => new() { RedirectIndex = redirectIndex, }; diff --git a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashStructuralCoordinator.cs b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashStructuralCoordinator.cs index 646db28..a031c8f 100644 --- a/src/ShellSyntaxTree/Internal/Bash/Parsing/BashStructuralCoordinator.cs +++ b/src/ShellSyntaxTree/Internal/Bash/Parsing/BashStructuralCoordinator.cs @@ -67,6 +67,7 @@ private static BashParseResult ParseStructured( CreateDependencySets(projection.Commands, analyzedFacts), CreateValueProvenanceSets(projection.Commands, analyzedFacts), CreateRedirectProvenanceSets(projection.Commands, analyzedFacts), + CreateRedirectAnalysisSets(projection.Commands, analyzedFacts), analyzedForInPlans); } @@ -122,6 +123,21 @@ private static IReadOnlyList return sets; } + private static IReadOnlyList CreateRedirectAnalysisSets( + IReadOnlyList commands, + Func factsFactory) + { + var sets = new RedirectAnalysisSet[commands.Count]; + for (var index = 0; index < sets.Length; index++) + { + var clause = commands[index].Clause; + var facts = factsFactory(new SimpleCommandSyntax { Clause = clause }); + sets[index] = new RedirectAnalysisSet(clause, facts.Redirects); + } + + return sets; + } + private static BashParseResult StructuralFailure( string source, string? reason, @@ -138,6 +154,7 @@ private static BashParseResult StructuralFailure( Array.Empty(), Array.Empty(), Array.Empty(), + Array.Empty(), Array.Empty()); private sealed class StructuralCoordinator @@ -1376,7 +1393,7 @@ private void RegisterFacts( } private static bool AreRedirectsComplete( - IReadOnlyList redirects) + IReadOnlyList redirects) { foreach (var redirect in redirects) { @@ -1485,9 +1502,18 @@ private bool TryRegisterDecodedFacts( return false; } + if (!TryFindRedirectAnalysis( + innerResult.RedirectAnalysisSets, + source.Clause, + out var redirectAnalysis)) + { + error = "decoded bash -c redirect analysis could not be mapped safely"; + return false; + } + _facts.Add(clonedClause, new CommandOccurrenceFacts { - Redirects = ClearDecodedHereDocumentSpans(source.Redirects), + Redirects = ClearDecodedHereDocumentSpans(redirectAnalysis), RedirectTargetProvenance = redirectProvenance, CwdPathDependencies = cwdPathDependencies, ValueProvenance = valueProvenance, @@ -1510,10 +1536,10 @@ private bool TryRegisterDecodedFacts( return true; } - private static IReadOnlyList ClearDecodedHereDocumentSpans( - IReadOnlyList redirects) + private static IReadOnlyList ClearDecodedHereDocumentSpans( + IReadOnlyList redirects) { - var rewritten = new RedirectAnalysis[redirects.Count]; + var rewritten = new RedirectAnalysisFacts[redirects.Count]; var changed = false; for (var index = 0; index < rewritten.Length; index++) { @@ -1583,6 +1609,24 @@ private static bool TryFindRedirectProvenance( return false; } + private static bool TryFindRedirectAnalysis( + IReadOnlyList analysisSets, + Clause clause, + out IReadOnlyList redirects) + { + for (var index = 0; index < analysisSets.Count; index++) + { + if (ReferenceEquals(analysisSets[index].Clause, clause)) + { + redirects = analysisSets[index].Redirects; + return true; + } + } + + redirects = Array.Empty(); + return false; + } + private static bool TryFindDependencies( IReadOnlyList dependencySets, Clause clause, @@ -2063,11 +2107,32 @@ private static bool TryCloneDecodedNode( Elements = ClauseElementProvenance.WithoutOuterSourceSpans( simple.Clause.Elements), }; + var attachedExecutionRegions = new List( + executionRegions.Count); + foreach (var executionRegion in executionRegions) + { + var hostIndex = executionRegion.HostClauseElementIndex; + if (executionRegion.Origin == ExecutionRegionOrigin.CommandArgument && + (hostIndex is null || + hostIndex < 0 || + hostIndex >= clonedClause.Elements.Count)) + { + return false; + } + + attachedExecutionRegions.Add(executionRegion with + { + HostArgument = hostIndex.HasValue + ? clonedClause.Elements[hostIndex.Value] + : null, + }); + } + clone = new SimpleCommandSyntax { Clause = clonedClause, Substitutions = substitutions, - ExecutionRegions = executionRegions, + ExecutionRegions = attachedExecutionRegions, }; referenceMap.Add(simple.Clause, clonedClause); return true; @@ -2264,6 +2329,7 @@ private static bool TryCloneDecodedNode( clone = new ExecutionRegionSyntax { Origin = executionRegion.Origin, + HostArgument = executionRegion.HostArgument, HostClauseElementIndex = executionRegion.HostClauseElementIndex, Phase = executionRegion.Phase, Timing = executionRegion.Timing, diff --git a/src/ShellSyntaxTree/Internal/PublicCollection.cs b/src/ShellSyntaxTree/Internal/PublicCollection.cs new file mode 100644 index 0000000..6b4f646 --- /dev/null +++ b/src/ShellSyntaxTree/Internal/PublicCollection.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2026 - 2026 Aaron Stannard +// +// ----------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace ShellSyntaxTree.Internal; + +internal static class PublicCollection +{ + internal static IReadOnlyList Copy(IReadOnlyList source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (source.Count == 0) + { + return Array.Empty(); + } + + var copy = new T[source.Count]; + for (var index = 0; index < source.Count; index++) + { + copy[index] = source[index]; + } + + return new ReadOnlyCollection(copy); + } + + internal static IReadOnlyList Copy(IEnumerable source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + return Copy(source is IReadOnlyList list + ? list + : new List(source)); + } +} diff --git a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshForEachValueAnalysis.cs b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshForEachValueAnalysis.cs index 449f693..4a28792 100644 --- a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshForEachValueAnalysis.cs +++ b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshForEachValueAnalysis.cs @@ -23,11 +23,11 @@ internal enum PwshIterationCardinality internal sealed record PwshForEachAnalysisPlan( string BindingName, - IReadOnlyList OrderedCandidates, + IReadOnlyList OrderedCandidates, int? AuthoredVisitCount, PwshIterationCardinality Cardinality, bool RequiresFixedPoint, - ShellValueDomain Summary); + ShellValueDomainFacts Summary); internal static class PwshForEachValueAnalysis { @@ -112,21 +112,21 @@ internal static PwshForEachAnalysisPlan CapturePlan( { return new PwshForEachAnalysisPlan( bindingName, - Array.Empty(), + Array.Empty(), AuthoredVisitCount: 0, PwshIterationCardinality.Never, RequiresFixedPoint: false, - ShellValueDomain.Unknown); + ShellValueDomainFacts.Unknown); } return isLiteralExpression ? new PwshForEachAnalysisPlan( bindingName, - new[] { ShellValueDomain.Unknown }, + new[] { ShellValueDomainFacts.Unknown }, AuthoredVisitCount: 1, PwshIterationCardinality.OneOrMore, RequiresFixedPoint: false, - ShellValueDomain.Unknown) + ShellValueDomainFacts.Unknown) : UnknownPlan(bindingName, PwshIterationCardinality.ZeroOrMore); } @@ -135,25 +135,25 @@ private static PwshForEachAnalysisPlan UnknownPlan( PwshIterationCardinality cardinality) => new( bindingName, - Array.Empty(), + Array.Empty(), AuthoredVisitCount: null, cardinality, RequiresFixedPoint: true, - ShellValueDomain.Unknown); + ShellValueDomainFacts.Unknown); private static bool TryCaptureLiteralArray( string raw, - out IReadOnlyList orderedCandidates, + out IReadOnlyList orderedCandidates, out int authoredVisitCount, out PwshIterationCardinality cardinality, out bool requiresFixedPoint, - out ShellValueDomain summary) + out ShellValueDomainFacts summary) { - orderedCandidates = Array.Empty(); + orderedCandidates = Array.Empty(); authoredVisitCount = 0; cardinality = PwshIterationCardinality.Never; requiresFixedPoint = false; - summary = ShellValueDomain.Unknown; + summary = ShellValueDomainFacts.Unknown; var index = 2; var end = raw.Length - 1; SkipWhitespace(raw, ref index, end); @@ -163,7 +163,7 @@ private static bool TryCaptureLiteralArray( } var values = new List(); - var ordered = new List(); + var ordered = new List(); var distinct = new HashSet(StringComparer.Ordinal); var allStrings = true; var summaryExceeded = false; @@ -183,7 +183,7 @@ private static bool TryCaptureLiteralArray( allStrings = false; if (count <= ShellAnalysisLimits.MaxValueCandidates) { - ordered.Add(ShellValueDomain.Unknown); + ordered.Add(ShellValueDomainFacts.Unknown); } } else @@ -226,7 +226,7 @@ private static bool TryCaptureLiteralArray( authoredVisitCount = count; requiresFixedPoint = count > ShellAnalysisLimits.MaxValueCandidates; orderedCandidates = requiresFixedPoint - ? Array.Empty() + ? Array.Empty() : ordered.ToArray(); if (allStrings && !summaryExceeded) { @@ -322,18 +322,18 @@ private static bool TryGetLiteralValue(ShellValue? value, out string literal) return true; } - private static ShellValueDomain Exact(string value) => new() + private static ShellValueDomainFacts Exact(string value) => new() { Kind = ShellValueDomainKind.Exact, Values = new[] { value }, }; - private static ShellValueDomain CreateFiniteDomain(IReadOnlyList values) => + private static ShellValueDomainFacts CreateFiniteDomain(IReadOnlyList values) => values.Count switch { - 0 => ShellValueDomain.Unknown, + 0 => ShellValueDomainFacts.Unknown, 1 => Exact(values[0]), - _ => new ShellValueDomain + _ => new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = Copy(values), @@ -365,7 +365,7 @@ internal static class PwshPersistentStateMutation internal static bool TryGetEffect( Clause clause, PwshDialect dialect, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, bool providerLocationUnknown, out bool unknownCwd) { @@ -416,7 +416,7 @@ internal static bool TryGetEffect( internal static bool MayEscapeChildScope( Clause clause, PwshDialect dialect, - IReadOnlyList effectiveArguments) + IReadOnlyList effectiveArguments) { var verb = GetCanonicalVerb(clause, dialect); if (verb is null) @@ -503,7 +503,7 @@ internal static bool MayEscapeChildScope( internal static bool MayEscapeChildRunspaceProcess( Clause clause, PwshDialect dialect, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, bool providerLocationUnknown) { var verb = GetCanonicalVerb(clause, dialect); @@ -540,7 +540,7 @@ internal static bool MayEscapeChildRunspaceProcess( internal static bool MayMutateAutomaticHome( Clause clause, PwshDialect dialect, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, bool providerLocationUnknown) { var verb = GetCanonicalVerb(clause, dialect); @@ -587,7 +587,7 @@ private static bool IsVariableCommand(string verb) => private static bool VariableCommandMayTargetAutomaticHome( string verb, Clause clause, - IReadOnlyList effectiveArguments) + IReadOnlyList effectiveArguments) { if (!TryGetAliasMutationNames( verb, @@ -654,7 +654,7 @@ internal static bool PreservesCommandResolution( internal static bool TryGetCommandResolutionMutation( Clause clause, PwshDialect dialect, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, bool providerLocationUnknown, out bool invalidatesAll, out IReadOnlyList commandNames) @@ -748,7 +748,7 @@ internal static bool TryGetCommandResolutionMutation( private static bool TryGetAliasMutationNames( string verb, Clause clause, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, out IReadOnlyList commandNames) { var names = new List(); @@ -832,7 +832,7 @@ private static bool TryAddInlineAliasCommandNames( ClauseElement element, int elementIndex, int separator, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, List names) { if (element.Kind is not (ArgKind.DynamicSkip or ArgKind.EnvVar)) @@ -869,7 +869,7 @@ private static bool TryAddInlineAliasCommandNames( private static bool TryGetProviderMutationNames( string verb, Clause clause, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, out bool invalidatesAll, out IReadOnlyList commandNames) { @@ -954,7 +954,7 @@ private static bool TryGetProviderMutationNames( private static bool TryAddProviderCommandNames( ClauseElement element, int elementIndex, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, List names, out bool selectedCommandProvider) { @@ -988,7 +988,7 @@ private static bool TryAddProviderCommandNames( private static bool TryAddCommandNames( ClauseElement element, int elementIndex, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, List names) { if (!TryGetExactElementValues( @@ -1014,7 +1014,7 @@ private static bool TryAddCommandNames( private static bool TryGetExactElementValues( ClauseElement element, int elementIndex, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, out IReadOnlyList values) { foreach (var effective in effectiveArguments) @@ -1254,7 +1254,7 @@ private static bool IsProvedChildLocalProviderMutation( private static bool DynamicArgumentMayEscapeChildScope( int elementIndex, - IReadOnlyList effectiveArguments) + IReadOnlyList effectiveArguments) { foreach (var effective in effectiveArguments) { @@ -1477,13 +1477,13 @@ internal static bool IsProviderStateMutation(string verb, Clause clause) => IsProviderStateMutation( verb, clause, - Array.Empty(), + Array.Empty(), failOnUnproved: false); private static bool IsProviderStateMutation( string verb, Clause clause, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, bool failOnUnproved, bool providerLocationUnknown = false) { @@ -1513,7 +1513,7 @@ private static bool IsProviderStateMutation( private static bool HasMutableOrUnprovedProviderTarget( string verb, Clause clause, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, bool failOnUnproved, StateProviderSelection selection = StateProviderSelection.AnyMutable, bool providerLocationUnknown = false) @@ -1587,7 +1587,7 @@ private static bool CanSelectProvider(ProviderParameterRole parameter) => private static bool IsMutableOrUnprovedProviderTarget( ClauseElement element, int elementIndex, - IReadOnlyList effectiveArguments, + IReadOnlyList effectiveArguments, bool failOnUnproved, StateProviderSelection selection, bool providerLocationUnknown) @@ -1645,7 +1645,7 @@ element.Resolved is not null && private static bool TargetDependsOnProviderLocation( ClauseElement element, int elementIndex, - IReadOnlyList effectiveArguments) + IReadOnlyList effectiveArguments) { if (IsProviderLocationIndependent(element.Value) || IsProviderLocationIndependent(element.Raw) || @@ -3013,8 +3013,8 @@ private void RecordFacts( SimpleCommandSyntax simple, AnalysisContext input, CommandOccurrenceFacts source, - IReadOnlyList effective, - IReadOnlyList redirects) + IReadOnlyList effective, + IReadOnlyList redirects) { var current = new CommandOccurrenceFacts { @@ -3051,7 +3051,7 @@ private void RecordFacts( }; } - private IReadOnlyList AnalyzeRedirects( + private IReadOnlyList AnalyzeRedirects( CommandOccurrenceFacts source, AnalysisContext input) { @@ -3061,7 +3061,7 @@ private IReadOnlyList AnalyzeRedirects( return source.Redirects; } - var rewritten = new RedirectAnalysis[source.Redirects.Count]; + var rewritten = new RedirectAnalysisFacts[source.Redirects.Count]; for (var index = 0; index < rewritten.Length; index++) { rewritten[index] = source.Redirects[index]; @@ -3082,7 +3082,7 @@ private IReadOnlyList AnalyzeRedirects( rewritten[provenance.RedirectIndex] = rewritten[provenance.RedirectIndex] with { - Target = ShellValueDomain.Unknown, + Target = ShellValueDomainFacts.Unknown, }; if (!TryGetRedirectEvaluationContext( provenance.InvocationScopeDepth, @@ -3146,7 +3146,7 @@ rewritten[provenance.RedirectIndex] with { Target = resolvedAll ? CreateDomain(values) - : ShellValueDomain.Unknown, + : ShellValueDomainFacts.Unknown, }; } @@ -3181,16 +3181,16 @@ private static bool IsRedirectHomePath(string value) => value[0] == '~' && (value.Length == 1 || value[1] is '/' or '\\'); - private static IReadOnlyList JoinRedirects( - IReadOnlyList left, - IReadOnlyList right) + private static IReadOnlyList JoinRedirects( + IReadOnlyList left, + IReadOnlyList right) { if (left.Count != right.Count) { - return Array.Empty(); + return Array.Empty(); } - var joined = new RedirectAnalysis[left.Count]; + var joined = new RedirectAnalysisFacts[left.Count]; for (var index = 0; index < joined.Length; index++) { var leftFact = left[index]; @@ -3202,7 +3202,7 @@ private static IReadOnlyList JoinRedirects( leftFact.IsPathRelevant != rightFact.IsPathRelevant || leftFact.HereDocument != rightFact.HereDocument) { - joined[index] = new RedirectAnalysis + joined[index] = new RedirectAnalysisFacts { RedirectIndex = leftFact.RedirectIndex, }; @@ -3219,22 +3219,22 @@ private static IReadOnlyList JoinRedirects( return joined; } - private static ShellValueDomain CreateDomain(IReadOnlyList values) => + private static ShellValueDomainFacts CreateDomain(IReadOnlyList values) => values.Count switch { - 1 => new ShellValueDomain + 1 => new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { values[0] }, }, _ when values.Count > 1 && values.Count <= ShellAnalysisLimits.MaxValueCandidates => - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = values, }, - _ => ShellValueDomain.Unknown, + _ => ShellValueDomainFacts.Unknown, }; private PwshFlowResult AnalyzeList(CommandListSyntax list, AnalysisContext input) @@ -3412,7 +3412,7 @@ private bool SimpleMayMutatePipelineState(SimpleCommandSyntax simple) return PwshPersistentStateMutation.TryGetEffect( simple.Clause, _options.Dialect, - Array.Empty(), + Array.Empty(), providerLocationUnknown: false, out _); } @@ -3869,14 +3869,14 @@ private bool TryGetElementDomain( SimpleCommandSyntax simple, int elementIndex, AnalysisContext input, - out ShellValueDomain domain) + out ShellValueDomainFacts domain) { if (TryGetElementValue(simple, elementIndex, 0, out var value)) { return input.TryEvaluateValue(value, out domain); } - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } @@ -3920,7 +3920,7 @@ private CommandOccurrenceFacts GetFacts(SimpleCommandSyntax simple) ? new CommandOccurrenceFacts { EffectiveArguments = source.EffectiveArguments, - WorkingDirectory = ShellValueDomain.Unknown, + WorkingDirectory = ShellValueDomainFacts.Unknown, Redirects = RewriteRedirectsForUnknownState(source), RedirectTargetProvenance = source.RedirectTargetProvenance, CwdPathDependencies = source.CwdPathDependencies, @@ -4048,8 +4048,8 @@ private SimpleCommandSyntax RewriteSimple( }; } - private static IReadOnlyList RewriteRedirectFacts( - IReadOnlyList source, + private static IReadOnlyList RewriteRedirectFacts( + IReadOnlyList source, IReadOnlyList provenance, Clause clause) { @@ -4058,7 +4058,7 @@ private static IReadOnlyList RewriteRedirectFacts( return source; } - var rewritten = new RedirectAnalysis[source.Count]; + var rewritten = new RedirectAnalysisFacts[source.Count]; for (var index = 0; index < rewritten.Length; index++) { var fact = source[index]; @@ -4079,8 +4079,8 @@ private static IReadOnlyList RewriteRedirectFacts( Target = hasProvenance ? fact.Target : compatibility.IsDynamicSkip - ? ShellValueDomain.Unknown - : new ShellValueDomain + ? ShellValueDomainFacts.Unknown + : new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { compatibility.Target }, @@ -4654,15 +4654,15 @@ private void RecordUnvisitedBindingArguments( { case SimpleCommandSyntax simple: var source = _factsFactory(simple); - var effective = new List(); + var effective = new List(); foreach (var provenance in source.ValueProvenance) { if (ReferencesBinding(provenance.Value, bindingName)) { - effective.Add(new EffectiveArgument + effective.Add(new EffectiveArgumentFacts { ClauseElementIndex = provenance.ClauseElementIndex, - Value = ShellValueDomain.Unknown, + Value = ShellValueDomainFacts.Unknown, }); } } @@ -4670,7 +4670,7 @@ private void RecordUnvisitedBindingArguments( _facts[simple.Clause] = new CommandOccurrenceFacts { EffectiveArguments = effective.ToArray(), - WorkingDirectory = ShellValueDomain.Unknown, + WorkingDirectory = ShellValueDomainFacts.Unknown, Redirects = RewriteRedirectsForUnknownState(source), RedirectTargetProvenance = source.RedirectTargetProvenance, CwdPathDependencies = source.CwdPathDependencies, @@ -4721,7 +4721,7 @@ private void RecordUnvisitedBindingArguments( } } - private IReadOnlyList RewriteRedirectsForUnknownState( + private IReadOnlyList RewriteRedirectsForUnknownState( CommandOccurrenceFacts source) { if (source.Redirects.Count == 0) @@ -4729,7 +4729,7 @@ private IReadOnlyList RewriteRedirectsForUnknownState( return source.Redirects; } - var redirects = new RedirectAnalysis[source.Redirects.Count]; + var redirects = new RedirectAnalysisFacts[source.Redirects.Count]; for (var index = 0; index < redirects.Length; index++) { var fact = source.Redirects[index]; @@ -4740,7 +4740,7 @@ private IReadOnlyList RewriteRedirectsForUnknownState( out var value)) { redirects[index] = fact.IsPathRelevant - ? fact with { Target = ShellValueDomain.Unknown } + ? fact with { Target = ShellValueDomainFacts.Unknown } : fact; continue; } @@ -4754,12 +4754,12 @@ private IReadOnlyList RewriteRedirectsForUnknownState( redirects[index] = fact with { Target = resolved.Resolved is not null && resolved.IsPath - ? new ShellValueDomain + ? new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { resolved.Resolved }, } - : ShellValueDomain.Unknown, + : ShellValueDomainFacts.Unknown, }; } @@ -4802,11 +4802,11 @@ private static bool ReferencesBinding(ShellValue value, string bindingName) return false; } - private static IReadOnlyList JoinEffectiveArguments( - IReadOnlyList left, - IReadOnlyList right) + private static IReadOnlyList JoinEffectiveArguments( + IReadOnlyList left, + IReadOnlyList right) { - var joined = new Dictionary(); + var joined = new Dictionary(); foreach (var argument in left) { joined[argument.ClauseElementIndex] = argument.Value; @@ -4823,10 +4823,10 @@ private static IReadOnlyList JoinEffectiveArguments( var indices = new List(joined.Keys); indices.Sort(); - var result = new EffectiveArgument[indices.Count]; + var result = new EffectiveArgumentFacts[indices.Count]; for (var index = 0; index < indices.Count; index++) { - result[index] = new EffectiveArgument + result[index] = new EffectiveArgumentFacts { ClauseElementIndex = indices[index], Value = joined[indices[index]], @@ -4836,25 +4836,25 @@ private static IReadOnlyList JoinEffectiveArguments( return result; } - private static ShellValueDomain JoinWorkingDirectories( - ShellValueDomain left, - ShellValueDomain right) => + private static ShellValueDomainFacts JoinWorkingDirectories( + ShellValueDomainFacts left, + ShellValueDomainFacts right) => left.Kind == ShellValueDomainKind.Exact && right.Kind == ShellValueDomainKind.Exact && left.Values.Count == 1 && right.Values.Count == 1 && string.Equals(left.Values[0], right.Values[0], StringComparison.Ordinal) ? left - : ShellValueDomain.Unknown; + : ShellValueDomainFacts.Unknown; - private static ShellValueDomain JoinDomains( - ShellValueDomain left, - ShellValueDomain right) + private static ShellValueDomainFacts JoinDomains( + ShellValueDomainFacts left, + ShellValueDomainFacts right) { if (left.Kind is not (ShellValueDomainKind.Exact or ShellValueDomainKind.FiniteSet) || right.Kind is not (ShellValueDomainKind.Exact or ShellValueDomainKind.FiniteSet)) { - return ShellValueDomain.Unknown; + return ShellValueDomainFacts.Unknown; } var values = new List(); @@ -4873,7 +4873,7 @@ right.Kind is not (ShellValueDomainKind.Exact or ShellValueDomainKind.FiniteSet) { if (values.Count == ShellAnalysisLimits.MaxValueCandidates) { - return ShellValueDomain.Unknown; + return ShellValueDomainFacts.Unknown; } values.Add(value); @@ -4882,29 +4882,29 @@ right.Kind is not (ShellValueDomainKind.Exact or ShellValueDomainKind.FiniteSet) return values.Count switch { - 1 => new ShellValueDomain + 1 => new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = values.ToArray(), }, - > 1 => new ShellValueDomain + > 1 => new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = values.ToArray(), }, - _ => ShellValueDomain.Unknown, + _ => ShellValueDomainFacts.Unknown, }; } private static string NormalizePath(string path) => path.Replace('\\', '/'); - private IReadOnlyList CreateEffectiveArguments( + private IReadOnlyList CreateEffectiveArguments( IReadOnlyList provenance, Clause clause, AnalysisContext context, bool includeUnresolved) { - var effective = new List(); + var effective = new List(); foreach (var value in provenance) { if (value.ClauseElementIndex >= 0 && @@ -4917,7 +4917,7 @@ private IReadOnlyList CreateEffectiveArguments( if (context.TryAnalyzeEffectiveValue(value.Value, out var domain)) { - effective.Add(new EffectiveArgument + effective.Add(new EffectiveArgumentFacts { ClauseElementIndex = value.ClauseElementIndex, Value = domain, @@ -4935,7 +4935,7 @@ private IReadOnlyList CreateEffectiveArguments( out domain) || IsPolicySensitiveValue(clause, value)) { - effective.Add(new EffectiveArgument + effective.Add(new EffectiveArgumentFacts { ClauseElementIndex = value.ClauseElementIndex, Value = domain, @@ -4943,19 +4943,19 @@ private IReadOnlyList CreateEffectiveArguments( } else if (includeUnresolved && ContainsExpansion(value.Value)) { - effective.Add(new EffectiveArgument + effective.Add(new EffectiveArgumentFacts { ClauseElementIndex = value.ClauseElementIndex, - Value = ShellValueDomain.Unknown, + Value = ShellValueDomainFacts.Unknown, }); } } else if (includeUnresolved && ContainsExpansion(value.Value)) { - effective.Add(new EffectiveArgument + effective.Add(new EffectiveArgumentFacts { ClauseElementIndex = value.ClauseElementIndex, - Value = ShellValueDomain.Unknown, + Value = ShellValueDomainFacts.Unknown, }); } } @@ -5121,7 +5121,7 @@ private bool TryAnalyzeParserKnownValue( ShellValue value, bool? usesNativeBinding, AnalysisContext context, - out ShellValueDomain domain) + out ShellValueDomainFacts domain) { var homeDirectory = PwshResolver.GetHomeDirectory(_options); var composed = new StringBuilder(value.Decoded.Length); @@ -5139,7 +5139,7 @@ private bool TryAnalyzeParserKnownValue( if (fragment.Kind != ShellValueFragmentKind.Expansion || fragment.Expansion is not ShellExpansionReference expansion) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } @@ -5160,7 +5160,7 @@ private bool TryAnalyzeParserKnownValue( if (usesNativeBinding is null) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } @@ -5172,7 +5172,7 @@ private bool TryAnalyzeParserKnownValue( if (!context.ConfiguredHomeAvailable || homeDirectory.Length == 0) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } @@ -5192,13 +5192,13 @@ private bool TryAnalyzeParserKnownValue( { if (usesNativeBinding is null) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } if (usesNativeBinding == true) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } } @@ -5218,14 +5218,14 @@ private bool TryAnalyzeParserKnownValue( (fragment.AllowedTransforms & ShellLexicalTransform.Variable) == 0 || homeDirectory.Length == 0) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } composed.Append(homeDirectory); } - domain = new ShellValueDomain + domain = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { composed.ToString() }, @@ -5297,7 +5297,7 @@ private static bool ContainsReference(IReadOnlyList clauses, Clause expe private sealed class BindingFrame { - internal BindingFrame(string name, ShellValueDomain domain) + internal BindingFrame(string name, ShellValueDomainFacts domain) { Name = name; Domain = domain; @@ -5305,7 +5305,7 @@ internal BindingFrame(string name, ShellValueDomain domain) internal string Name { get; } - internal ShellValueDomain Domain { get; } + internal ShellValueDomainFacts Domain { get; } } private readonly struct AnalysisContext @@ -5424,7 +5424,7 @@ internal AnalysisContext Invalidate( { unknown[index] = new BindingFrame( _bindings[index].Name, - ShellValueDomain.Unknown); + ShellValueDomainFacts.Unknown); } return new AnalysisContext( @@ -5640,7 +5640,7 @@ internal AnalysisContext WithCwd(string? workingDirectory) => internal AnalysisContext WithBinding( string name, - ShellValueDomain domain, + ShellValueDomainFacts domain, bool canPromote) { var bindings = new List(_bindings.Count + 1); @@ -5654,7 +5654,7 @@ internal AnalysisContext WithBinding( bindings.Add(new BindingFrame( name, - canPromote ? domain : ShellValueDomain.Unknown)); + canPromote ? domain : ShellValueDomainFacts.Unknown)); return new AnalysisContext( WorkingDirectory, CanPromote, @@ -5669,10 +5669,10 @@ internal AnalysisContext WithBinding( bindings); } - internal ShellValueDomain ToWorkingDirectoryDomain() => + internal ShellValueDomainFacts ToWorkingDirectoryDomain() => WorkingDirectory is null - ? ShellValueDomain.Unknown - : new ShellValueDomain + ? ShellValueDomainFacts.Unknown + : new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { WorkingDirectory }, @@ -5680,7 +5680,7 @@ WorkingDirectory is null internal bool TryEvaluateValue( ShellValue value, - out ShellValueDomain domain) + out ShellValueDomainFacts domain) { var literal = true; foreach (var fragment in value.Fragments) @@ -5694,7 +5694,7 @@ internal bool TryEvaluateValue( if (literal) { - domain = new ShellValueDomain + domain = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { value.Decoded }, @@ -5707,7 +5707,7 @@ internal bool TryEvaluateValue( internal bool TryAnalyzeEffectiveValue( ShellValue value, - out ShellValueDomain domain) + out ShellValueDomainFacts domain) { var referenced = new List(); var unresolved = false; @@ -5740,13 +5740,13 @@ fragment.Expansion is not ShellExpansionReference expansion || if (referenced.Count == 0) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return false; } if (unresolved) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return true; } @@ -5755,7 +5755,7 @@ fragment.Expansion is not ShellExpansionReference expansion || if (binding.Domain.Kind is not ( ShellValueDomainKind.Exact or ShellValueDomainKind.FiniteSet)) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return true; } } @@ -5771,17 +5771,17 @@ fragment.Expansion is not ShellExpansionReference expansion || candidates, distinct)) { - domain = ShellValueDomain.Unknown; + domain = ShellValueDomainFacts.Unknown; return true; } domain = candidates.Count == 1 - ? new ShellValueDomain + ? new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { candidates[0] }, } - : new ShellValueDomain + : new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = candidates.ToArray(), @@ -5963,7 +5963,7 @@ internal static AnalysisContext Join( bindings.Add(new BindingFrame( name, leftBinding is null || rightBinding is null - ? ShellValueDomain.Unknown + ? ShellValueDomainFacts.Unknown : JoinDomains(leftBinding.Domain, rightBinding.Domain))); } @@ -6068,8 +6068,8 @@ internal static AnalysisContext Widen( } private static bool DomainEquals( - ShellValueDomain left, - ShellValueDomain right) + ShellValueDomainFacts left, + ShellValueDomainFacts right) { if (left.Kind != right.Kind || left.Values.Count != right.Values.Count) { diff --git a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshRedirectAnalysis.cs b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshRedirectAnalysis.cs index f0c05b1..86911d2 100644 --- a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshRedirectAnalysis.cs +++ b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshRedirectAnalysis.cs @@ -15,11 +15,11 @@ namespace ShellSyntaxTree.Internal.Pwsh.Parsing; /// internal static class PwshRedirectAnalysis { - internal static IReadOnlyList Analyze(Clause clause) + internal static IReadOnlyList Analyze(Clause clause) { if (clause.Redirects.Count == 0) { - return Array.Empty(); + return Array.Empty(); } var elements = new List(clause.Redirects.Count); @@ -31,7 +31,7 @@ internal static IReadOnlyList Analyze(Clause clause) } } - var result = new RedirectAnalysis[clause.Redirects.Count]; + var result = new RedirectAnalysisFacts[clause.Redirects.Count]; for (var index = 0; index < result.Length; index++) { result[index] = index < elements.Count @@ -42,7 +42,7 @@ internal static IReadOnlyList Analyze(Clause clause) return result; } - private static RedirectAnalysis Analyze( + private static RedirectAnalysisFacts Analyze( int redirectIndex, Redirect compatibility, ClauseElement element) @@ -58,7 +58,7 @@ private static RedirectAnalysis Analyze( if (operation == RedirectOperation.DescriptorDuplicate) { - return new RedirectAnalysis + return new RedirectAnalysisFacts { RedirectIndex = redirectIndex, Source = source, @@ -73,22 +73,18 @@ private static RedirectAnalysis Analyze( compatibility.Target.Equals("$null", StringComparison.OrdinalIgnoreCase)) { // The public v0.3 operation vocabulary has no discard-sink member. - // Preserve the source coordinate, but keep the fact incomplete. - return new RedirectAnalysis - { - RedirectIndex = redirectIndex, - Source = source, - }; + // No known source may pair with an unresolved operation. + return Incomplete(redirectIndex); } - return new RedirectAnalysis + return new RedirectAnalysisFacts { RedirectIndex = redirectIndex, Source = source, Operation = operation, Target = compatibility.IsDynamicSkip - ? ShellValueDomain.Unknown - : new ShellValueDomain + ? ShellValueDomainFacts.Unknown + : new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { compatibility.Target }, @@ -102,11 +98,11 @@ private static RedirectAnalysis Analyze( private static bool TryReadOperator( string raw, - out RedirectSource source, + out RedirectSourceFacts source, out RedirectOperation operation, out int length) { - source = new RedirectSource { Kind = RedirectSourceKind.Default }; + source = new RedirectSourceFacts { Kind = RedirectSourceKind.Default }; operation = RedirectOperation.Unknown; length = 0; if (string.IsNullOrEmpty(raw)) @@ -117,7 +113,7 @@ private static bool TryReadOperator( var operatorStart = 0; if (raw[0] == '*') { - source = new RedirectSource + source = new RedirectSourceFacts { Kind = RedirectSourceKind.PowerShellAllStreams, }; @@ -125,7 +121,7 @@ private static bool TryReadOperator( } else if (raw[0] is >= '1' and <= '6') { - source = new RedirectSource + source = new RedirectSourceFacts { Kind = RedirectSourceKind.Descriptor, Descriptor = raw[0] - '0', @@ -159,11 +155,11 @@ private static bool TryReadOperator( return true; } - source = new RedirectSource(); + source = new RedirectSourceFacts(); return false; } - private static RedirectAnalysis Incomplete(int redirectIndex) => new() + private static RedirectAnalysisFacts Incomplete(int redirectIndex) => new() { RedirectIndex = redirectIndex, }; diff --git a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshStructuralCoordinator.cs b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshStructuralCoordinator.cs index 63f0409..f824336 100644 --- a/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshStructuralCoordinator.cs +++ b/src/ShellSyntaxTree/Internal/Pwsh/Parsing/PwshStructuralCoordinator.cs @@ -753,6 +753,7 @@ private bool TryParseCommandExecutionRegions( parsed.Add(new ExecutionRegionSyntax { Origin = ExecutionRegionOrigin.CommandArgument, + HostArgument = clause.Elements[blockBinding.HostClauseElementIndex], HostClauseElementIndex = blockBinding.HostClauseElementIndex, Phase = blockBinding.Phase, Timing = blockBinding.Timing, @@ -2266,6 +2267,27 @@ private static bool TryCloneDecodedNode( } } + var attachedExecutionRegions = new List( + executionRegions.Count); + foreach (var executionRegion in executionRegions) + { + var hostIndex = executionRegion.HostClauseElementIndex; + if (executionRegion.Origin == ExecutionRegionOrigin.CommandArgument && + (hostIndex is null || + hostIndex < 0 || + hostIndex >= elements.Count)) + { + return false; + } + + attachedExecutionRegions.Add(executionRegion with + { + HostArgument = hostIndex.HasValue + ? elements[hostIndex.Value] + : null, + }); + } + clone = new SimpleCommandSyntax { Clause = simple.Clause with @@ -2277,7 +2299,7 @@ private static bool TryCloneDecodedNode( IsCommandStringWrapped = true, }, Substitutions = substitutions, - ExecutionRegions = executionRegions, + ExecutionRegions = attachedExecutionRegions, }; return true; case PipelineSyntax pipeline: @@ -2412,6 +2434,7 @@ private static bool TryCloneDecodedNode( clone = new ExecutionRegionSyntax { Origin = executionRegion.Origin, + HostArgument = executionRegion.HostArgument, HostClauseElementIndex = executionRegion.HostClauseElementIndex, Phase = executionRegion.Phase, Timing = executionRegion.Timing, @@ -2479,7 +2502,7 @@ private static bool IsStructurallyComplete(SimpleCommandSyntax simple) } private static bool AreRedirectsComplete( - IReadOnlyList redirects) + IReadOnlyList redirects) { foreach (var redirect in redirects) { diff --git a/src/ShellSyntaxTree/Internal/ShellConditionalSyntax.cs b/src/ShellSyntaxTree/Internal/ShellConditionalSyntax.cs new file mode 100644 index 0000000..a3c399d --- /dev/null +++ b/src/ShellSyntaxTree/Internal/ShellConditionalSyntax.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2026 - 2026 Aaron Stannard +// +// ----------------------------------------------------------------------- +using System; +using System.Collections.Generic; + +namespace ShellSyntaxTree; + +internal sealed record ConditionLoopSyntax : ShellSyntaxNode +{ + private protected override object LibraryOwnership => this; + + internal ConditionLoopKind LoopKind { get; init; } + + internal ShellBlockSyntax Condition { get; init; } = new(); + + internal ShellBlockSyntax Body { get; init; } = new(); +} + +internal enum ConditionLoopKind +{ + Unknown, + While, + Until, +} + +internal sealed record ConditionalSyntax : ShellSyntaxNode +{ + private protected override object LibraryOwnership => this; + + internal IReadOnlyList Branches { get; init; } = + Array.Empty(); + + internal ShellBlockSyntax? Else { get; init; } +} + +internal sealed record ConditionalBranchSyntax : ShellSyntaxNode +{ + private protected override object LibraryOwnership => this; + + internal ShellBlockSyntax Condition { get; init; } = new(); + + internal ShellBlockSyntax Body { get; init; } = new(); +} diff --git a/src/ShellSyntaxTree/ParsedCommand.cs b/src/ShellSyntaxTree/ParsedCommand.cs index 4dda637..89d147e 100644 --- a/src/ShellSyntaxTree/ParsedCommand.cs +++ b/src/ShellSyntaxTree/ParsedCommand.cs @@ -5,6 +5,7 @@ // ----------------------------------------------------------------------- using System; using System.Collections.Generic; +using ShellSyntaxTree.Internal; namespace ShellSyntaxTree; @@ -13,6 +14,8 @@ namespace ShellSyntaxTree; /// public sealed record ParsedCommand { + private IReadOnlyList _commands = Array.Empty(); + /// The original input string, verbatim. public string Source { get; init; } = ""; @@ -21,14 +24,17 @@ public sealed record ParsedCommand /// source ranges; decoded wrapper nodes use unavailable ranges unless an /// exact outer mapping exists. /// - public ShellBlockSyntax Syntax { get; init; } = new(); + public ShellBlockSyntax Syntax { get; internal init; } = new(); /// /// Canonical authorization projection containing every authored simple /// command that may execute exactly once in deterministic source order. /// - public IReadOnlyList Commands { get; init; } = - Array.Empty(); + public IReadOnlyList Commands + { + get => _commands; + internal init => _commands = PublicCollection.Copy(value); + } /// /// Conservative v0.2 compatibility projection. Existing simple-command diff --git a/src/ShellSyntaxTree/RedirectAnalysis.cs b/src/ShellSyntaxTree/RedirectAnalysis.cs index 2fa34d5..076bb8f 100644 --- a/src/ShellSyntaxTree/RedirectAnalysis.cs +++ b/src/ShellSyntaxTree/RedirectAnalysis.cs @@ -5,110 +5,288 @@ // ----------------------------------------------------------------------- namespace ShellSyntaxTree; -/// Explicit analysis for one compatibility redirect. -public sealed record RedirectAnalysis +/// A closed description of a redirect's source stream. +public abstract record RedirectSource { - /// Gets the index into . - public int RedirectIndex { get; init; } = -1; + private protected RedirectSource() + { + } - /// Gets the redirect's source stream. - public RedirectSource Source { get; init; } = new(); + private protected abstract object LibraryOwnership { get; } + + internal RedirectSourceKind Kind => this switch + { + Unknown => RedirectSourceKind.Unknown, + Default => RedirectSourceKind.Default, + Descriptor => RedirectSourceKind.Descriptor, + PowerShellAllStreams => RedirectSourceKind.PowerShellAllStreams, + _ => RedirectSourceKind.Unknown, + }; + + internal int? DescriptorValue => (this as Descriptor)?.Value; + + /// The source stream could not be proved. + public sealed record Unknown : RedirectSource + { + internal Unknown() + { + } + + private protected override object LibraryOwnership => this; + } + + /// The shell's operation-specific default stream applies. + public sealed record Default : RedirectSource + { + internal Default() + { + } + + private protected override object LibraryOwnership => this; + } + + /// An explicit numeric descriptor applies. + public sealed record Descriptor : RedirectSource + { + internal Descriptor(int value) => Value = value; + + private protected override object LibraryOwnership => this; + + /// Gets the numeric descriptor. + public int Value { get; } + } + + /// PowerShell's all-streams selector applies. + public sealed record PowerShellAllStreams : RedirectSource + { + internal PowerShellAllStreams() + { + } + + private protected override object LibraryOwnership => this; + } +} + +/// Base record for closed, parser-owned redirect alternatives. +public abstract record RedirectAnalysis +{ + private protected RedirectAnalysis() + { + } + + private protected abstract object LibraryOwnership { get; } + + internal int RedirectIndex { get; init; } = -1; - /// Gets the redirect operation. - public RedirectOperation Operation { get; init; } + internal RedirectOperation Operation => this switch + { + FileRedirectAnalysis { Mode: FileRedirectMode.Input } => RedirectOperation.FileInput, + FileRedirectAnalysis { Mode: FileRedirectMode.Output } => RedirectOperation.FileOutput, + FileRedirectAnalysis { Mode: FileRedirectMode.Append } => RedirectOperation.FileAppend, + FileRedirectAnalysis { Mode: FileRedirectMode.CombinedOutput } => + RedirectOperation.CombinedOutput, + FileRedirectAnalysis { Mode: FileRedirectMode.CombinedOutputAppend } => + RedirectOperation.CombinedOutputAppend, + DescriptorDuplicateRedirectAnalysis => RedirectOperation.DescriptorDuplicate, + DescriptorMoveRedirectAnalysis => RedirectOperation.DescriptorMove, + DescriptorCloseRedirectAnalysis => RedirectOperation.DescriptorClose, + HereDocumentRedirectAnalysis => RedirectOperation.HereDocument, + HereStringRedirectAnalysis => RedirectOperation.HereString, + _ => RedirectOperation.Unknown, + }; - /// Gets a static target descriptor, when proved. - public int? TargetDescriptor { get; init; } + internal int? TargetDescriptor => this switch + { + DescriptorDuplicateRedirectAnalysis duplicate => duplicate.TargetDescriptor, + DescriptorMoveRedirectAnalysis move => move.TargetDescriptor, + _ => null, + }; - /// Gets the target path or data proof. - public ShellValueDomain Target { get; init; } = ShellValueDomain.Unknown; + internal ShellValueDomain Target => this switch + { + FileRedirectAnalysis file => file.Target, + HereStringRedirectAnalysis hereString => hereString.Data, + _ => new ShellValueDomain.Unknown(), + }; - /// Gets heredoc-specific facts for a heredoc operation. - public HereDocumentAnalysis? HereDocument { get; init; } + internal HereDocumentAnalysis? HereDocument => + (this as HereDocumentRedirectAnalysis)?.Document; - /// Gets whether the target is a filesystem policy input. - public bool IsPathRelevant { get; init; } + internal bool IsPathRelevant => this is FileRedirectAnalysis; + + /// Gets the exact compatibility redirect represented by this analysis. + public Redirect Authored { get; internal init; } = null!; + + /// Gets the redirect's source stream. + public RedirectSource Source { get; internal init; } = null!; /// Gets whether the parser-owned redirect analysis is complete. - public bool IsComplete { get; init; } + public bool IsComplete { get; internal init; } +} + +/// A redirect to or from a filesystem path. +public sealed record FileRedirectAnalysis : RedirectAnalysis +{ + internal FileRedirectAnalysis(FileRedirectMode mode) => Mode = mode; + + private protected override object LibraryOwnership => this; + + /// Gets the file operation. + public FileRedirectMode Mode { get; } + + /// Gets the target path proof. + public new ShellValueDomain Target { get; internal init; } = null!; +} + +/// A completely delimited redirect whose semantics remain unproved. +public sealed record UnresolvedRedirectAnalysis : RedirectAnalysis +{ + internal UnresolvedRedirectAnalysis() + { + } + + private protected override object LibraryOwnership => this; +} + +/// Identifies a file redirect operation. +public enum FileRedirectMode +{ + Input, + Output, + Append, + CombinedOutput, + CombinedOutputAppend, +} + +/// Duplicates a source descriptor to a target descriptor. +public sealed record DescriptorDuplicateRedirectAnalysis : RedirectAnalysis +{ + internal DescriptorDuplicateRedirectAnalysis() + { + } + + private protected override object LibraryOwnership => this; + + public new int TargetDescriptor { get; internal init; } +} + +/// Moves a source descriptor to a target descriptor. +public sealed record DescriptorMoveRedirectAnalysis : RedirectAnalysis +{ + internal DescriptorMoveRedirectAnalysis() + { + } + + private protected override object LibraryOwnership => this; + + public new int TargetDescriptor { get; internal init; } +} + +/// Closes a source descriptor. +public sealed record DescriptorCloseRedirectAnalysis : RedirectAnalysis +{ + internal DescriptorCloseRedirectAnalysis() + { + } + + private protected override object LibraryOwnership => this; +} + +/// Supplies standard input from a Bash heredoc. +public sealed record HereDocumentRedirectAnalysis : RedirectAnalysis +{ + internal HereDocumentRedirectAnalysis() + { + } + + private protected override object LibraryOwnership => this; + + public HereDocumentAnalysis Document { get; internal init; } = null!; +} + +/// Supplies standard input from a Bash here string. +public sealed record HereStringRedirectAnalysis : RedirectAnalysis +{ + internal HereStringRedirectAnalysis() + { + } + + private protected override object LibraryOwnership => this; + + public ShellValueDomain Data { get; internal init; } = null!; } /// Authored heredoc delimiter, body, and expansion facts. public sealed record HereDocumentAnalysis { - /// Gets the authored delimiter. - public ShellSourceFragment Delimiter { get; init; } = new(); + internal HereDocumentAnalysis() + { + } - /// Gets the authored body. - public ShellSourceFragment Body { get; init; } = new(); + public ShellSourceFragment Delimiter { get; internal init; } = null!; - /// Gets whether the body is literal or expanding. - public HereDocumentExpansionMode ExpansionMode { get; init; } + public ShellSourceFragment Body { get; internal init; } = null!; - /// Gets whether leading body tabs are stripped. - public bool StripLeadingTabs { get; init; } + public HereDocumentExpansionMode ExpansionMode { get; internal init; } - /// Gets whether delimiter, body, and substitutions are complete. - public bool IsComplete { get; init; } + public bool StripLeadingTabs { get; internal init; } + + public bool IsComplete { get; internal init; } } -/// Identifies heredoc body expansion behavior. public enum HereDocumentExpansionMode { - /// The expansion behavior is unknown. Unknown, - /// The body is literal data. Literal, - /// The body performs shell expansion. Expand, } -/// A shell redirect's source stream. -public sealed record RedirectSource +/// Internal mutable-by-copy redirect facts produced by shell analyzers. +internal sealed record RedirectAnalysisFacts +{ + internal int RedirectIndex { get; init; } = -1; + + internal RedirectSourceFacts Source { get; init; } = new(); + + internal RedirectOperation Operation { get; init; } + + internal int? TargetDescriptor { get; init; } + + internal ShellValueDomainFacts Target { get; init; } = ShellValueDomainFacts.Unknown; + + internal HereDocumentAnalysis? HereDocument { get; init; } + + internal bool IsPathRelevant { get; init; } + + internal bool IsComplete { get; init; } +} + +internal sealed record RedirectSourceFacts { - /// Gets the source-stream kind. - public RedirectSourceKind Kind { get; init; } + internal RedirectSourceKind Kind { get; init; } - /// Gets the numeric descriptor for a descriptor source. - public int? Descriptor { get; init; } + internal int? Descriptor { get; init; } } -/// Identifies the source side of a redirect. -public enum RedirectSourceKind +internal enum RedirectSourceKind { - /// The source is unknown. Unknown, - /// The shell's default source stream applies. Default, - /// An explicit numeric descriptor applies. Descriptor, - /// PowerShell's all-streams selector applies. PowerShellAllStreams, } -/// Identifies a redirect's shell operation. -public enum RedirectOperation +internal enum RedirectOperation { - /// The operation is unknown. Unknown, - /// Read input from a file. FileInput, - /// Write output to a file. FileOutput, - /// Append output to a file. FileAppend, - /// Duplicate one descriptor to another. DescriptorDuplicate, - /// Close a descriptor. DescriptorClose, - /// Move and close a descriptor. DescriptorMove, - /// Write standard output and error to one file. CombinedOutput, - /// Append standard output and error to one file. CombinedOutputAppend, - /// Supply heredoc data. HereDocument, - /// Supply Bash here-string data. HereString, } diff --git a/src/ShellSyntaxTree/ShellAnalysisLimits.cs b/src/ShellSyntaxTree/ShellAnalysisLimits.cs index 3ff0b45..d4c3fe2 100644 --- a/src/ShellSyntaxTree/ShellAnalysisLimits.cs +++ b/src/ShellSyntaxTree/ShellAnalysisLimits.cs @@ -6,14 +6,14 @@ namespace ShellSyntaxTree; /// Fixed resource and proof bounds for structured shell analysis. -public static class ShellAnalysisLimits +internal static class ShellAnalysisLimits { /// Gets the maximum number of finite value candidates. - public static int MaxValueCandidates => 32; + internal static int MaxValueCandidates => 32; /// Gets the maximum structural container depth. - public static int MaxStructuralNesting => 16; + internal static int MaxStructuralNesting => 16; /// Gets the maximum decoded command-string wrapper depth. - public static int MaxWrapperRecursionDepth => 5; + internal static int MaxWrapperRecursionDepth => 5; } diff --git a/src/ShellSyntaxTree/ShellSyntaxNode.cs b/src/ShellSyntaxTree/ShellSyntaxNode.cs index 6007f6c..7cfdac2 100644 --- a/src/ShellSyntaxTree/ShellSyntaxNode.cs +++ b/src/ShellSyntaxTree/ShellSyntaxNode.cs @@ -5,364 +5,293 @@ // ----------------------------------------------------------------------- using System; using System.Collections.Generic; +using ShellSyntaxTree.Internal; namespace ShellSyntaxTree; -/// -/// Base record for the library-owned authored syntax hierarchy. -/// +/// Base record for the library-owned authored syntax hierarchy. public abstract record ShellSyntaxNode { private protected ShellSyntaxNode() { } - private protected abstract bool IsLibraryOwnedNode { get; } + private protected abstract object LibraryOwnership { get; } - /// Gets the stable syntax discriminant. - public abstract ShellSyntaxKind Kind { get; } - - /// Gets the zero-based start in , when exact. - public int? SourceStart { get; init; } - - /// Gets the source length, when exact. - public int? SourceLength { get; init; } + internal ShellSyntaxKind Kind => this switch + { + ShellBlockSyntax => ShellSyntaxKind.Block, + SimpleCommandSyntax => ShellSyntaxKind.SimpleCommand, + PipelineSyntax => ShellSyntaxKind.Pipeline, + CommandListSyntax => ShellSyntaxKind.CommandList, + GroupSyntax => ShellSyntaxKind.Group, + ForEachSyntax => ShellSyntaxKind.ForEach, + CommandSubstitutionSyntax => ShellSyntaxKind.CommandSubstitution, + ExecutionRegionSyntax => ShellSyntaxKind.ExecutionRegion, + _ => ShellSyntaxKind.Unknown, + }; + + public int? SourceStart { get; internal init; } + + public int? SourceLength { get; internal init; } } -/// Identifies a library-owned syntax-node shape. -public enum ShellSyntaxKind +internal enum ShellSyntaxKind { - /// The kind is unknown to the consumer. Unknown, - /// An ordered statement block. Block, - /// An existing simple-command leaf. SimpleCommand, - /// An ordered pipeline. Pipeline, - /// A command list with authored operators. CommandList, - /// A grouped command region. Group, - /// A foreach-style loop. ForEach, - /// A condition-controlled loop. - ConditionLoop, - /// A conditional statement. - Conditional, - /// One conditional branch. - ConditionalBranch, - /// A command substitution. CommandSubstitution, - /// An authored body activated by shell or command semantics. ExecutionRegion, } /// An ordered block of authored statements. public sealed record ShellBlockSyntax : ShellSyntaxNode { - private protected override bool IsLibraryOwnedNode => true; + private IReadOnlyList _statements = Array.Empty(); + + internal ShellBlockSyntax() + { + } - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.Block; + private protected override object LibraryOwnership => this; - /// Gets the statements in authored order. - public IReadOnlyList Statements { get; init; } = - Array.Empty(); + public IReadOnlyList Statements + { + get => _statements; + internal init => _statements = PublicCollection.Copy(value); + } } /// A simple command backed by the existing compatibility leaf. public sealed record SimpleCommandSyntax : ShellSyntaxNode { - private protected override bool IsLibraryOwnedNode => true; + private IReadOnlyList _substitutions = + Array.Empty(); + private IReadOnlyList _executionRegions = + Array.Empty(); - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.SimpleCommand; + internal SimpleCommandSyntax() + { + } - /// Gets the shared simple-command leaf. - public Clause Clause { get; init; } = new(); + private protected override object LibraryOwnership => this; - /// - /// Gets executable command substitutions evaluated for this command's - /// authored words and redirects, in source order. - /// - public IReadOnlyList Substitutions { get; init; } = - Array.Empty(); + public Clause Clause { get; internal init; } = new(); - /// - /// Gets execution-bearing regions bound to this command's arguments, in - /// authored order. - /// - public IReadOnlyList ExecutionRegions { get; init; } = - Array.Empty(); + public IReadOnlyList Substitutions + { + get => _substitutions; + internal init => _substitutions = PublicCollection.Copy(value); + } + + public IReadOnlyList ExecutionRegions + { + get => _executionRegions; + internal init => _executionRegions = PublicCollection.Copy(value); + } } /// An ordered pipeline. public sealed record PipelineSyntax : ShellSyntaxNode { - private protected override bool IsLibraryOwnedNode => true; + private IReadOnlyList _stages = Array.Empty(); - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.Pipeline; + internal PipelineSyntax() + { + } + + private protected override object LibraryOwnership => this; - /// Gets the stages in authored order. - public IReadOnlyList Stages { get; init; } = - Array.Empty(); + public IReadOnlyList Stages + { + get => _stages; + internal init => _stages = PublicCollection.Copy(value); + } } /// A command list preserving authored operator relationships. public sealed record CommandListSyntax : ShellSyntaxNode { - private protected override bool IsLibraryOwnedNode => true; + private IReadOnlyList _items = + Array.Empty(); - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.CommandList; + internal CommandListSyntax() + { + } - /// Gets the list items in authored order. - public IReadOnlyList Items { get; init; } = - Array.Empty(); + private protected override object LibraryOwnership => this; + + public IReadOnlyList Items + { + get => _items; + internal init => _items = PublicCollection.Copy(value); + } } /// One command-list item and its preceding authored operator. public sealed record CommandListItemSyntax { - /// Gets the authored relationship to the preceding item. - public CompoundOperator Operator { get; init; } + internal CommandListItemSyntax() + { + } - /// Gets the command structure for this item. - public ShellSyntaxNode Command { get; init; } = new ShellBlockSyntax(); + public CompoundOperator Operator { get; internal init; } + + public ShellSyntaxNode Command { get; internal init; } = null!; } /// A grouped command region. public sealed record GroupSyntax : ShellSyntaxNode { - private protected override bool IsLibraryOwnedNode => true; + internal GroupSyntax() + { + } - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.Group; + private protected override object LibraryOwnership => this; - /// Gets the group's shell-state behavior. - public ShellGroupKind GroupKind { get; init; } + public ShellGroupKind GroupKind { get; internal init; } - /// Gets the grouped body. - public ShellBlockSyntax Body { get; init; } = new(); + public ShellBlockSyntax Body { get; internal init; } = null!; } -/// Identifies whether a group shares or isolates shell state. public enum ShellGroupKind { - /// The state behavior is unknown. Unknown, - /// The group executes in the current shell scope. CurrentScope, - /// The group executes in an isolated shell scope. IsolatedScope, } /// A foreach-style loop with a shell-specific iterable spelling. public sealed record ForEachSyntax : ShellSyntaxNode { - private protected override bool IsLibraryOwnedNode => true; - - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.ForEach; - - /// Gets the loop binding. - public LoopBindingSyntax Binding { get; init; } = new(); - - /// Gets the shell-specific authored iterable. - public ShellSourceFragment Iterable { get; init; } = new(); - - /// Gets commands discovered while producing iterator values. - public ShellBlockSyntax IteratorCommands { get; init; } = new(); - - /// Gets the loop body. - public ShellBlockSyntax Body { get; init; } = new(); -} - -/// A normalized loop binding and its authored source. -public sealed record LoopBindingSyntax -{ - /// Gets the normalized binding name. - public string Name { get; init; } = ""; + private LoopBindingSyntax _binding = new(); - /// Gets the authored binding source. - public ShellSourceFragment Source { get; init; } = new(); -} - -/// An authored source fragment with an exact-or-unavailable outer range. -public sealed record ShellSourceFragment -{ - /// Gets the authored raw spelling. - public string Raw { get; init; } = ""; + internal ForEachSyntax() + { + } - /// Gets the zero-based outer source start, when exact. - public int? SourceStart { get; init; } + private protected override object LibraryOwnership => this; - /// Gets the outer source length, when exact. - public int? SourceLength { get; init; } -} - -/// A while- or until-style loop. -public sealed record ConditionLoopSyntax : ShellSyntaxNode -{ - private protected override bool IsLibraryOwnedNode => true; + public string BindingName + { + get => _binding.Name; + internal init => _binding = _binding with { Name = value }; + } - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.ConditionLoop; + public ShellSourceFragment BindingSource + { + get => _binding.Source; + internal init => _binding = _binding with { Source = value }; + } - /// Gets the loop kind. - public ConditionLoopKind LoopKind { get; init; } + internal LoopBindingSyntax Binding + { + get => _binding; + init => _binding = value; + } - /// Gets the executable condition region. - public ShellBlockSyntax Condition { get; init; } = new(); + public ShellSourceFragment Iterable { get; internal init; } = null!; - /// Gets the loop body. - public ShellBlockSyntax Body { get; init; } = new(); -} + public ShellBlockSyntax IteratorCommands { get; internal init; } = null!; -/// Identifies the condition-loop behavior. -public enum ConditionLoopKind -{ - /// The loop kind is unknown. - Unknown, - /// The loop continues while its condition succeeds. - While, - /// The loop continues until its condition succeeds. - Until, + public ShellBlockSyntax Body { get; internal init; } = null!; } -/// A conditional statement with authored branches. -public sealed record ConditionalSyntax : ShellSyntaxNode +internal sealed record LoopBindingSyntax { - private protected override bool IsLibraryOwnedNode => true; - - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.Conditional; - - /// Gets the conditional branches in authored order. - public IReadOnlyList Branches { get; init; } = - Array.Empty(); + internal string Name { get; init; } = ""; - /// Gets the optional else body. - public ShellBlockSyntax? Else { get; init; } + internal ShellSourceFragment Source { get; init; } = new(); } -/// One conditional condition and body pair. -public sealed record ConditionalBranchSyntax : ShellSyntaxNode +/// An authored source fragment with an exact-or-unavailable outer range. +public sealed record ShellSourceFragment { - private protected override bool IsLibraryOwnedNode => true; + internal ShellSourceFragment() + { + } - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.ConditionalBranch; + public string Raw { get; internal init; } = ""; - /// Gets the executable condition region. - public ShellBlockSyntax Condition { get; init; } = new(); + public int? SourceStart { get; internal init; } - /// Gets the branch body. - public ShellBlockSyntax Body { get; init; } = new(); + public int? SourceLength { get; internal init; } } /// A command substitution whose produced value is analyzed separately. public sealed record CommandSubstitutionSyntax : ShellSyntaxNode { - private protected override bool IsLibraryOwnedNode => true; + internal CommandSubstitutionSyntax() + { + } - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.CommandSubstitution; + private protected override object LibraryOwnership => this; - /// Gets the commands inside the substitution. - public ShellBlockSyntax Body { get; init; } = new(); + public ShellBlockSyntax Body { get; internal init; } = null!; } /// An authored body whose activation is described by shell semantics. public sealed record ExecutionRegionSyntax : ShellSyntaxNode { - private protected override bool IsLibraryOwnedNode => true; + internal ExecutionRegionSyntax() + { + } + + private protected override object LibraryOwnership => this; - /// - public override ShellSyntaxKind Kind => ShellSyntaxKind.ExecutionRegion; + public ExecutionRegionOrigin Origin { get; internal init; } - /// Gets the syntax or binding that introduces the region. - public ExecutionRegionOrigin Origin { get; init; } + public ClauseElement? HostArgument { get; internal init; } - /// - /// Gets the index of the bound script-block token in the owning command's - /// , or null for a direct shell invocation. - /// - public int? HostClauseElementIndex { get; init; } + internal int? HostClauseElementIndex { get; init; } - /// Gets the semantic activation phase. - public ExecutionRegionPhase Phase { get; init; } + public ExecutionRegionPhase Phase { get; internal init; } - /// Gets the relationship between registration and execution time. - public ExecutionRegionTiming Timing { get; init; } + public ExecutionRegionTiming Timing { get; internal init; } - /// Gets how activations relate to one authored region. - public ExecutionRegionCardinality Cardinality { get; init; } + public ExecutionRegionCardinality Cardinality { get; internal init; } - /// Gets the recursively parsed region body. - public ShellBlockSyntax Body { get; init; } = new(); + public ShellBlockSyntax Body { get; internal init; } = null!; } -/// Identifies how an authored execution region is introduced. public enum ExecutionRegionOrigin { - /// The introducing syntax or binding is unknown. Unknown, - /// A direct PowerShell call-operator script block: & { ... }. DirectCall, - /// A direct PowerShell dot-source script block: . { ... }. DotSource, - /// A script block bound to an argument of a host command. CommandArgument, } -/// Identifies an execution region's semantic phase. public enum ExecutionRegionPhase { - /// The phase is unknown. Unknown, - /// The primary body of an invocation. Main, - /// An initialization body. Initialization, - /// A pipeline begin body. Begin, - /// A pipeline process body. Process, - /// A pipeline end body. End, - /// A per-input filter body. Filter, - /// An externally triggered action body. Action, - /// An argument-completion body. Completion, } -/// Identifies when an execution region may run. public enum ExecutionRegionTiming { - /// The timing is unknown. Unknown, - /// The body completes as part of the containing invocation. Synchronous, - /// Instances or the containing continuation may overlap. Concurrent, - /// Registration and a later trigger are distinct. Deferred, } -/// Identifies how often one authored execution region may activate. public enum ExecutionRegionCardinality { - /// The activation cardinality is unknown. Unknown, - /// The body activates once per proved host invocation. Once, - /// The body activates once per input object. OncePerInputObject, - /// The body may activate zero or more times. ZeroOrMore, } diff --git a/src/ShellSyntaxTree/ShellSyntaxProjection.cs b/src/ShellSyntaxTree/ShellSyntaxProjection.cs index 1ec6cf9..d53295e 100644 --- a/src/ShellSyntaxTree/ShellSyntaxProjection.cs +++ b/src/ShellSyntaxTree/ShellSyntaxProjection.cs @@ -16,13 +16,13 @@ namespace ShellSyntaxTree; /// internal sealed class CommandOccurrenceFacts { - internal IReadOnlyList EffectiveArguments { get; init; } = - Array.Empty(); + internal IReadOnlyList EffectiveArguments { get; init; } = + Array.Empty(); - internal ShellValueDomain WorkingDirectory { get; init; } = ShellValueDomain.Unknown; + internal ShellValueDomainFacts WorkingDirectory { get; init; } = ShellValueDomainFacts.Unknown; - internal IReadOnlyList Redirects { get; init; } = - Array.Empty(); + internal IReadOnlyList Redirects { get; init; } = + Array.Empty(); internal IReadOnlyList RedirectTargetProvenance { get; init; } = Array.Empty(); @@ -38,6 +38,26 @@ internal sealed class CommandOccurrenceFacts internal bool IsComplete { get; init; } } +internal sealed record EffectiveArgumentFacts +{ + internal int ClauseElementIndex { get; init; } = -1; + + internal ShellValueDomainFacts Value { get; init; } = ShellValueDomainFacts.Unknown; +} + +internal sealed record ShellValueDomainFacts +{ + internal static ShellValueDomainFacts Unknown { get; } = new(); + + internal ShellValueDomainKind Kind { get; init; } + + internal IReadOnlyList Values { get; init; } = Array.Empty(); + + internal string? Pattern { get; init; } + + internal string? CoveringDirectory { get; init; } +} + /// /// Retains the complete shell-owned value fragments for one authored clause /// element so execution-state analysis can re-evaluate it per visit. @@ -187,11 +207,6 @@ group.Body is not null && role, nextDepth), ForEachSyntax forEach => TryVisitForEach(forEach, nextDepth), - ConditionLoopSyntax loop => - loop.LoopKind is ConditionLoopKind.While or ConditionLoopKind.Until && - TryVisitConditionLoop(loop, nextDepth), - ConditionalSyntax conditional => TryVisitConditional(conditional, role, nextDepth), - ConditionalBranchSyntax branch => TryVisitConditionalBranch(branch, nextDepth), CommandSubstitutionSyntax substitution => TryVisitCommandSubstitution( substitution, @@ -279,7 +294,7 @@ simple.Clause is null || if (!TryCopyFacts( simple.Clause, facts, - out var effectiveArguments, + out var arguments, out var workingDirectory, out var redirects)) { @@ -291,7 +306,7 @@ simple.Clause is null || Clause = simple.Clause, ImmediateRole = role, Ancestry = _ancestry.ToArray(), - EffectiveArguments = effectiveArguments, + Arguments = arguments, WorkingDirectory = workingDirectory, Redirects = redirects, IsComplete = facts.IsComplete && _structuralContextIsComplete && @@ -443,92 +458,6 @@ forEach.IteratorCommands is null || structuralDepth); } - private bool TryVisitConditionLoop( - ConditionLoopSyntax loop, - int structuralDepth) - { - if (loop.Condition is null || loop.Body is null) - { - return false; - } - - return TryVisitChild( - loop, - loop.Condition, - CommandAncestryRegion.Condition, - childIndex: null, - CommandOccurrenceRole.Condition, - structuralDepth) && - TryVisitChild( - loop, - loop.Body, - CommandAncestryRegion.LoopBody, - childIndex: null, - CommandOccurrenceRole.LoopBody, - structuralDepth); - } - - private bool TryVisitConditional( - ConditionalSyntax conditional, - CommandOccurrenceRole role, - int structuralDepth) - { - if (conditional.Branches is null || conditional.Branches.Count == 0) - { - return false; - } - - for (var index = 0; index < conditional.Branches.Count; index++) - { - var branch = conditional.Branches[index]; - if (branch is null || - !TryVisitChild( - conditional, - branch, - CommandAncestryRegion.Branch, - index, - role, - structuralDepth)) - { - return false; - } - } - - return conditional.Else is null || - TryVisitChild( - conditional, - conditional.Else, - CommandAncestryRegion.Branch, - conditional.Branches.Count, - CommandOccurrenceRole.Branch, - structuralDepth); - } - - private bool TryVisitConditionalBranch( - ConditionalBranchSyntax branch, - int structuralDepth) - { - if (branch.Condition is null || branch.Body is null) - { - return false; - } - - return TryVisitChild( - branch, - branch.Condition, - CommandAncestryRegion.Condition, - childIndex: null, - CommandOccurrenceRole.Condition, - structuralDepth) && - TryVisitChild( - branch, - branch.Body, - CommandAncestryRegion.Branch, - childIndex: null, - CommandOccurrenceRole.Branch, - structuralDepth); - } - private bool TryVisitChild( ShellSyntaxNode ancestor, ShellSyntaxNode child, @@ -539,11 +468,9 @@ private bool TryVisitChild( { _ancestry.Add(new CommandAncestryFrame { - AncestorKind = ancestor.Kind, + Ancestor = ancestor, Region = region, ChildIndex = childIndex, - SourceStart = ancestor.SourceStart, - SourceLength = ancestor.SourceLength, }); var succeeded = TryVisit( child, @@ -558,8 +485,6 @@ private bool TryVisitChild( private static bool CountsTowardStructuralDepth(ShellSyntaxNode node) => node is ForEachSyntax or - ConditionLoopSyntax or - ConditionalSyntax or GroupSyntax or CommandSubstitutionSyntax or ExecutionRegionSyntax; @@ -574,11 +499,15 @@ private static bool AreValidAttachedExecutionRegions( var region = executionRegions[index]; if (region is null || !IsValidExecutionRegion(region, isAttachedToSimple: true) || + region.HostArgument is null || region.HostClauseElementIndex >= clause.Elements.Count || !hostCoordinates.Add(region.HostClauseElementIndex!.Value) || - clause.Elements[region.HostClauseElementIndex.Value].Role != + !ReferenceEquals( + region.HostArgument, + clause.Elements[region.HostClauseElementIndex.Value]) || + region.HostArgument.Role != ClauseElementRole.Argument || - clause.Elements[region.HostClauseElementIndex.Value].Kind != + region.HostArgument.Kind != ArgKind.DynamicSkip) { return false; @@ -601,9 +530,13 @@ executionRegion.Body is not null && executionRegion.Origin switch { ExecutionRegionOrigin.DirectCall or ExecutionRegionOrigin.DotSource => - !isAttachedToSimple && executionRegion.HostClauseElementIndex is null, + !isAttachedToSimple && + executionRegion.HostArgument is null && + executionRegion.HostClauseElementIndex is null, ExecutionRegionOrigin.CommandArgument => - isAttachedToSimple && executionRegion.HostClauseElementIndex >= 0, + isAttachedToSimple && + executionRegion.HostArgument is not null && + executionRegion.HostClauseElementIndex >= 0, _ => false, }; @@ -630,12 +563,12 @@ private static bool IsExecutionRegionFactComplete( private static bool TryCopyFacts( Clause clause, CommandOccurrenceFacts facts, - out IReadOnlyList effectiveArguments, + out IReadOnlyList arguments, out ShellValueDomain workingDirectory, out IReadOnlyList redirects) { - effectiveArguments = Array.Empty(); - workingDirectory = ShellValueDomain.Unknown; + arguments = Array.Empty(); + workingDirectory = new ShellValueDomain.Unknown(); redirects = Array.Empty(); if (facts is null || facts.EffectiveArguments is null || @@ -654,10 +587,12 @@ facts.WorkingDirectory.Kind is not ( return false; } - effectiveArguments = Copy(facts.EffectiveArguments); - workingDirectory = facts.WorkingDirectory; - redirects = Copy(facts.Redirects); - return true; + workingDirectory = ToPublicDomain(facts.WorkingDirectory); + return TryCreateAnalyzedArguments( + clause, + facts.EffectiveArguments, + out arguments) && + TryCreateRedirectAnalyses(clause, facts.Redirects, out redirects); } private static bool IsValidClauseShape(Clause clause) => @@ -672,9 +607,267 @@ clause.Elements is not null && !ContainsNull(clause.Redirects) && !ContainsNull(clause.Elements); + private static bool TryCreateAnalyzedArguments( + Clause clause, + IReadOnlyList effective, + out IReadOnlyList arguments) + { + arguments = Array.Empty(); + var authoredArguments = new List(); + for (var index = 0; index < clause.Args.Count; index++) + { + if (!clause.Args[index].IsCwdAttribution) + { + authoredArguments.Add(clause.Args[index]); + } + } + + var elementIndices = new List(); + for (var index = 0; index < clause.Elements.Count; index++) + { + if (clause.Elements[index].Role == ClauseElementRole.Argument) + { + elementIndices.Add(index); + } + } + + if (authoredArguments.Count == 0) + { + return elementIndices.Count == 0; + } + + var domains = new Dictionary(); + for (var index = 0; index < effective.Count; index++) + { + domains.Add(effective[index].ClauseElementIndex, effective[index].Value); + } + + var projected = new List(authoredArguments.Count); + var argumentIndex = 0; + for (var elementOffset = 0; elementOffset < elementIndices.Count; elementOffset++) + { + if (argumentIndex >= authoredArguments.Count) + { + return false; + } + + var elementIndex = elementIndices[elementOffset]; + var element = clause.Elements[elementIndex]; + var remainingArguments = authoredArguments.Count - argumentIndex; + var remainingElements = elementIndices.Count - elementOffset; + var count = remainingArguments > remainingElements && + argumentIndex + 1 < authoredArguments.Count && + IsInlineArgumentPair( + element, + authoredArguments[argumentIndex], + authoredArguments[argumentIndex + 1]) + ? 2 + : 1; + + var effectiveOffset = count == 1 || + !authoredArguments[argumentIndex].IsFlag + ? 0 + : 1; + for (var offset = 0; offset < count; offset++) + { + var argument = authoredArguments[argumentIndex + offset]; + var hasEffectiveValue = domains.TryGetValue( + elementIndex, + out var domain) && + offset == effectiveOffset; + var value = hasEffectiveValue + ? ToPublicDomain(domain!) + : DefaultArgumentDomain(argument, element, count == 1); + projected.Add(new AnalyzedArgument + { + Argument = argument, + Element = element, + Value = value, + HasEffectiveValue = hasEffectiveValue, + }); + } + + argumentIndex += count; + } + + if (argumentIndex != authoredArguments.Count) + { + return false; + } + + arguments = projected; + return true; + } + + private static bool IsInlineArgumentPair( + ClauseElement element, + Arg first, + Arg second) => + IsInlineArgumentPair(element.Value, first.Raw, second.Raw) || + IsInlineArgumentPair(element.Raw, first.Raw, second.Raw); + + private static bool IsInlineArgumentPair( + string combined, + string first, + string second) => + string.Equals(combined, first + "=" + second, StringComparison.Ordinal) || + string.Equals(combined, first + ":" + second, StringComparison.Ordinal); + + private static ShellValueDomain DefaultArgumentDomain( + Arg argument, + ClauseElement element, + bool isOnlyArgumentForElement) => + argument.Kind is ArgKind.DynamicSkip or ArgKind.EnvVar or ArgKind.Glob + ? new ShellValueDomain.Unknown() + : new ShellValueDomain.Exact( + isOnlyArgumentForElement ? element.Value : argument.Raw); + + private static bool TryCreateRedirectAnalyses( + Clause clause, + IReadOnlyList facts, + out IReadOnlyList redirects) + { + redirects = Array.Empty(); + if (facts.Count != clause.Redirects.Count) + { + return false; + } + + var projected = new RedirectAnalysis[facts.Count]; + for (var index = 0; index < facts.Count; index++) + { + var fact = facts[index]; + if (fact.RedirectIndex < 0 || + fact.RedirectIndex >= projected.Length || + projected[fact.RedirectIndex] is not null || + !TryCreateRedirectAnalysis( + clause.Redirects[fact.RedirectIndex], + fact, + out projected[fact.RedirectIndex])) + { + return false; + } + } + + redirects = projected; + return true; + } + + private static bool TryCreateRedirectAnalysis( + Redirect authored, + RedirectAnalysisFacts fact, + out RedirectAnalysis analysis) + { + var source = ToPublicSource(fact.Source); + analysis = source is RedirectSource.Unknown + ? new UnresolvedRedirectAnalysis() + : fact.Operation switch + { + RedirectOperation.FileInput => new FileRedirectAnalysis(FileRedirectMode.Input) + { + Target = ToPublicDomain(fact.Target), + }, + RedirectOperation.FileOutput => new FileRedirectAnalysis(FileRedirectMode.Output) + { + Target = ToPublicDomain(fact.Target), + }, + RedirectOperation.FileAppend => new FileRedirectAnalysis(FileRedirectMode.Append) + { + Target = ToPublicDomain(fact.Target), + }, + RedirectOperation.CombinedOutput => + new FileRedirectAnalysis(FileRedirectMode.CombinedOutput) + { + Target = ToPublicDomain(fact.Target), + }, + RedirectOperation.CombinedOutputAppend => + new FileRedirectAnalysis(FileRedirectMode.CombinedOutputAppend) + { + Target = ToPublicDomain(fact.Target), + }, + RedirectOperation.DescriptorDuplicate when fact.TargetDescriptor.HasValue => + new DescriptorDuplicateRedirectAnalysis + { + TargetDescriptor = fact.TargetDescriptor.Value, + }, + RedirectOperation.DescriptorMove when fact.TargetDescriptor.HasValue => + new DescriptorMoveRedirectAnalysis + { + TargetDescriptor = fact.TargetDescriptor.Value, + }, + RedirectOperation.DescriptorClose => new DescriptorCloseRedirectAnalysis(), + RedirectOperation.HereDocument when fact.HereDocument is not null => + new HereDocumentRedirectAnalysis { Document = fact.HereDocument }, + RedirectOperation.HereString => new HereStringRedirectAnalysis + { + Data = ToPublicDomain(fact.Target), + }, + _ => new UnresolvedRedirectAnalysis(), + }; + analysis = analysis with + { + RedirectIndex = fact.RedirectIndex, + Authored = authored, + Source = source, + IsComplete = fact.IsComplete, + }; + return IsValidSourceOperationPair(analysis); + } + + private static RedirectSource ToPublicSource(RedirectSourceFacts source) => + source.Kind switch + { + RedirectSourceKind.Default => new RedirectSource.Default(), + RedirectSourceKind.Descriptor when source.Descriptor.HasValue => + new RedirectSource.Descriptor(source.Descriptor.Value), + RedirectSourceKind.PowerShellAllStreams => + new RedirectSource.PowerShellAllStreams(), + _ => new RedirectSource.Unknown(), + }; + + private static bool IsValidSourceOperationPair(RedirectAnalysis analysis) => + (analysis.Source, analysis) switch + { + (RedirectSource.Unknown, UnresolvedRedirectAnalysis) => + !analysis.IsComplete, + (RedirectSource.Default, FileRedirectAnalysis) => true, + (RedirectSource.Default, DescriptorDuplicateRedirectAnalysis) => true, + (RedirectSource.Default, DescriptorMoveRedirectAnalysis) => true, + (RedirectSource.Default, DescriptorCloseRedirectAnalysis) => true, + (RedirectSource.Default, HereDocumentRedirectAnalysis) => true, + (RedirectSource.Default, HereStringRedirectAnalysis) => true, + (RedirectSource.Descriptor, FileRedirectAnalysis + { Mode: FileRedirectMode.Input or + FileRedirectMode.Output or + FileRedirectMode.Append }) => true, + (RedirectSource.Descriptor, DescriptorDuplicateRedirectAnalysis) => true, + (RedirectSource.Descriptor, DescriptorMoveRedirectAnalysis) => true, + (RedirectSource.Descriptor, DescriptorCloseRedirectAnalysis) => true, + (RedirectSource.Descriptor, HereDocumentRedirectAnalysis) => true, + (RedirectSource.Descriptor, HereStringRedirectAnalysis) => true, + (RedirectSource.PowerShellAllStreams, + FileRedirectAnalysis + { Mode: FileRedirectMode.Output or FileRedirectMode.Append }) => true, + (RedirectSource.PowerShellAllStreams, + DescriptorDuplicateRedirectAnalysis { TargetDescriptor: 1 }) => true, + (RedirectSource.PowerShellAllStreams, _) => false, + _ => false, + }; + + private static ShellValueDomain ToPublicDomain(ShellValueDomainFacts domain) => + domain.Kind switch + { + ShellValueDomainKind.Exact => new ShellValueDomain.Exact(domain.Values[0]), + ShellValueDomainKind.FiniteSet => new ShellValueDomain.FiniteSet(domain.Values), + ShellValueDomainKind.Pattern => new ShellValueDomain.PathPattern( + domain.Pattern!, + domain.CoveringDirectory!), + _ => new ShellValueDomain.Unknown(), + }; + private static bool AreValidEffectiveArguments( Clause clause, - IReadOnlyList arguments) + IReadOnlyList arguments) { var coordinates = new HashSet(); for (var index = 0; index < arguments.Count; index++) @@ -696,7 +889,7 @@ argument.Value is null || private static bool AreValidRedirects( Clause clause, - IReadOnlyList redirects, + IReadOnlyList redirects, bool occurrenceIsComplete) { if (occurrenceIsComplete && redirects.Count != clause.Redirects.Count) @@ -721,7 +914,7 @@ private static bool AreValidRedirects( return true; } - private static bool IsValidRedirect(RedirectAnalysis redirect) + private static bool IsValidRedirect(RedirectAnalysisFacts redirect) { if (redirect.Source is null || redirect.Target is null || @@ -777,7 +970,7 @@ redirect.HereDocument is null && }; } - private static bool IsValidRedirectSource(RedirectSource source) => + private static bool IsValidRedirectSource(RedirectSourceFacts source) => source.Kind switch { RedirectSourceKind.Unknown => source.Descriptor is null, @@ -811,7 +1004,7 @@ private static bool IsValidSpan(int? sourceStart, int? sourceLength) => sourceStart.HasValue == sourceLength.HasValue && (!sourceStart.HasValue || sourceStart >= 0 && sourceLength >= 0); - private static bool IsValidValueDomain(ShellValueDomain domain) + private static bool IsValidValueDomain(ShellValueDomainFacts domain) { if (domain.Values is null || ContainsNull(domain.Values)) { @@ -856,17 +1049,6 @@ private static bool AreDistinct(IReadOnlyList values) return true; } - private static T[] Copy(IReadOnlyList items) - { - var copy = new T[items.Count]; - for (var index = 0; index < items.Count; index++) - { - copy[index] = items[index]; - } - - return copy; - } - private static bool ContainsNull(IReadOnlyList items) where T : class { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/AstAssert.cs b/tests/ShellSyntaxTree.Tests/Corpus/AstAssert.cs index b95e8d2..f54b4af 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/AstAssert.cs +++ b/tests/ShellSyntaxTree.Tests/Corpus/AstAssert.cs @@ -3,6 +3,7 @@ // Copyright (C) 2026 - 2026 Aaron Stannard // // ----------------------------------------------------------------------- +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -205,7 +206,7 @@ private static void ValidateExpectedSyntax( string prefix) { if (expected.Count == 0 || - expected[0].Kind != ShellSyntaxKind.Block || + expected[0].Kind != "Block" || expected[0].ParentIndex is not null || expected[0].Region != CommandAncestryRegion.Unknown || expected[0].ChildIndex is not null || @@ -218,10 +219,10 @@ expected[0].ChildIndex is not null || for (var index = 0; index < expected.Count; index++) { var node = expected[index]; - var isExecutionRegion = node.Kind == ShellSyntaxKind.ExecutionRegion; - if (node.Kind == ShellSyntaxKind.Unknown || - (node.Kind == ShellSyntaxKind.SimpleCommand) != node.ClauseIndex.HasValue || - (node.Kind == ShellSyntaxKind.Group) != node.GroupKind.HasValue || + var isExecutionRegion = node.Kind == "ExecutionRegion"; + if (node.Kind == "Unknown" || + (node.Kind == "SimpleCommand") != node.ClauseIndex.HasValue || + (node.Kind == "Group") != node.GroupKind.HasValue || isExecutionRegion != node.ExecutionOrigin.HasValue || isExecutionRegion != node.ExecutionPhase.HasValue || isExecutionRegion != node.ExecutionTiming.HasValue || @@ -258,55 +259,44 @@ expected[0].ChildIndex is not null || var parent = expected[node.ParentIndex.Value]; var relationshipIsValid = parent.Kind switch { - ShellSyntaxKind.Block => + "Block" => node.Region == (node.ParentIndex == 0 ? CommandAncestryRegion.Root : CommandAncestryRegion.Statement) && node.ChildIndex.HasValue, - ShellSyntaxKind.SimpleCommand => - ((node.Kind == ShellSyntaxKind.CommandSubstitution && + "SimpleCommand" => + ((node.Kind == "CommandSubstitution" && node.Region == CommandAncestryRegion.Substitution) || - (node.Kind == ShellSyntaxKind.ExecutionRegion && + (node.Kind == "ExecutionRegion" && node.Region == CommandAncestryRegion.ExecutionRegion)) && node.ChildIndex.HasValue, - ShellSyntaxKind.Pipeline => + "Pipeline" => node.Region == CommandAncestryRegion.PipelineStage && node.ChildIndex.HasValue, - ShellSyntaxKind.CommandList => + "CommandList" => node.Region == CommandAncestryRegion.Statement && node.ChildIndex.HasValue && node.ListOperator.HasValue, - ShellSyntaxKind.Group => + "Group" => node.Region == CommandAncestryRegion.GroupBody && node.ChildIndex is null, - ShellSyntaxKind.ForEach => + "ForEach" => (node.Region is CommandAncestryRegion.Iterator or CommandAncestryRegion.LoopBody) && node.ChildIndex is null, - ShellSyntaxKind.ConditionLoop => - (node.Region is CommandAncestryRegion.Condition or - CommandAncestryRegion.LoopBody) && - node.ChildIndex is null, - ShellSyntaxKind.Conditional => - node.Region == CommandAncestryRegion.Branch && - node.ChildIndex.HasValue, - ShellSyntaxKind.ConditionalBranch => - (node.Region is CommandAncestryRegion.Condition or - CommandAncestryRegion.Branch) && - node.ChildIndex is null, - ShellSyntaxKind.CommandSubstitution => + "CommandSubstitution" => node.Region == CommandAncestryRegion.Substitution, - ShellSyntaxKind.ExecutionRegion => - node.Kind == ShellSyntaxKind.Block && + "ExecutionRegion" => + node.Kind == "Block" && node.Region == CommandAncestryRegion.ExecutionRegion, _ => false, }; var executionRegionPlacementIsValid = - node.Kind != ShellSyntaxKind.ExecutionRegion || + node.Kind != "ExecutionRegion" || (node.ExecutionOrigin == ExecutionRegionOrigin.CommandArgument) == - (parent.Kind == ShellSyntaxKind.SimpleCommand); + (parent.Kind == "SimpleCommand"); var executionRegionCoordinateIsValid = - node.Kind != ShellSyntaxKind.ExecutionRegion || + node.Kind != "ExecutionRegion" || node.ExecutionOrigin != ExecutionRegionOrigin.CommandArgument || IsValidExpectedHostCoordinate( node, @@ -317,7 +307,7 @@ expected[0].ChildIndex is not null || if (!relationshipIsValid || !executionRegionPlacementIsValid || !executionRegionCoordinateIsValid || - parent.Kind != ShellSyntaxKind.CommandList && node.ListOperator.HasValue) + parent.Kind != "CommandList" && node.ListOperator.HasValue) { throw new XunitException( prefix + $"syntax[{index}] has an invalid parent relationship"); @@ -425,8 +415,8 @@ private static void AssertCommandsEqual( { var expectedFrame = expectedFrames[frameIndex]; var actualFrame = observed.Ancestry[frameIndex]; - if (expectedFrame.AncestorKind == ShellSyntaxKind.Unknown || - actualFrame.AncestorKind == ShellSyntaxKind.Unknown || + if (expectedFrame.AncestorKind == "Unknown" || + SyntaxKind(actualFrame.Ancestor) == "Unknown" || expectedFrame.Region == CommandAncestryRegion.Unknown || actualFrame.Region == CommandAncestryRegion.Unknown) { @@ -434,41 +424,44 @@ private static void AssertCommandsEqual( prefix + $"commands[{index}].ancestry[{frameIndex}] contains an unknown enum"); } - if (expectedFrame.AncestorKind != actualFrame.AncestorKind || + if (expectedFrame.AncestorKind != SyntaxKind(actualFrame.Ancestor) || expectedFrame.Region != actualFrame.Region || expectedFrame.ChildIndex != actualFrame.ChildIndex || - expectedFrame.SourceStart != actualFrame.SourceStart || - expectedFrame.SourceLength != actualFrame.SourceLength) + expectedFrame.SourceStart != actualFrame.Ancestor.SourceStart || + expectedFrame.SourceLength != actualFrame.Ancestor.SourceLength) { throw new XunitException( prefix + $"commands[{index}].ancestry[{frameIndex}] differs"); } } - if (wanted.EffectiveArguments is not null) + if (wanted.Arguments is not null) { - if (wanted.EffectiveArguments.Count != observed.EffectiveArguments.Count) + if (wanted.Arguments.Count != observed.Arguments.Count) { throw new XunitException( - prefix + $"commands[{index}].effectiveArguments.count differs"); + prefix + $"commands[{index}].arguments.count differs"); } - for (var effectiveIndex = 0; - effectiveIndex < wanted.EffectiveArguments.Count; - effectiveIndex++) + for (var argumentIndex = 0; + argumentIndex < wanted.Arguments.Count; + argumentIndex++) { - var expectedEffective = wanted.EffectiveArguments[effectiveIndex]; - var actualEffective = observed.EffectiveArguments[effectiveIndex]; - if (expectedEffective.ClauseElementIndex != actualEffective.ClauseElementIndex) + var expectedArgument = wanted.Arguments[argumentIndex]; + var actualArgument = observed.Arguments[argumentIndex]; + if (expectedArgument.ClauseArgumentIndex != + FindArgumentIndex(observed.Clause, actualArgument.Argument) || + expectedArgument.ClauseElementIndex != + FindElementIndex(observed.Clause, actualArgument.Element)) { throw new XunitException( - prefix + $"commands[{index}].effectiveArguments[{effectiveIndex}].clauseElementIndex differs"); + prefix + $"commands[{index}].arguments[{argumentIndex}] coordinate differs"); } AssertValueDomainEqual( - expectedEffective.Value, - actualEffective.Value, - prefix + $"commands[{index}].effectiveArguments[{effectiveIndex}].value"); + expectedArgument.Value, + actualArgument.Value, + prefix + $"commands[{index}].arguments[{argumentIndex}].value"); } } @@ -505,32 +498,34 @@ private static void AssertRedirectAnalysesEqual( { var wanted = expected[index]; var observed = actual[index]; - if (wanted.RedirectIndex != observed.RedirectIndex || - wanted.SourceKind != observed.Source.Kind || - wanted.SourceDescriptor != observed.Source.Descriptor || - wanted.Operation != observed.Operation || - wanted.TargetDescriptor != observed.TargetDescriptor || - wanted.IsPathRelevant != observed.IsPathRelevant || + if (wanted.RedirectIndex != index || + wanted.Kind != observed.GetType().Name || + wanted.SourceKind != observed.Source.GetType().Name || + wanted.SourceDescriptor != + (observed.Source as RedirectSource.Descriptor)?.Value || + wanted.FileMode != (observed as FileRedirectAnalysis)?.Mode || + wanted.TargetDescriptor != GetTargetDescriptor(observed) || wanted.IsComplete != observed.IsComplete) { throw new XunitException( $"{path}[{index}] differs: expected index={wanted.RedirectIndex}, " + $"source={wanted.SourceKind}/{wanted.SourceDescriptor}, " - + $"operation={wanted.Operation}, targetDescriptor={wanted.TargetDescriptor}, " - + $"pathRelevant={wanted.IsPathRelevant}, complete={wanted.IsComplete}; " - + $"actual index={observed.RedirectIndex}, " - + $"source={observed.Source.Kind}/{observed.Source.Descriptor}, " - + $"operation={observed.Operation}, targetDescriptor={observed.TargetDescriptor}, " - + $"pathRelevant={observed.IsPathRelevant}, complete={observed.IsComplete}"); + + $"kind={wanted.Kind}, mode={wanted.FileMode}, " + + $"targetDescriptor={wanted.TargetDescriptor}, complete={wanted.IsComplete}; " + + $"actual source={observed.Source.GetType().Name}, " + + $"kind={observed.GetType().Name}, complete={observed.IsComplete}"); } - AssertValueDomainEqual( - wanted.Target, - observed.Target, - $"{path}[{index}].target"); + if (wanted.Value is not null) + { + AssertValueDomainEqual( + wanted.Value, + GetRedirectValue(observed), + $"{path}[{index}].value"); + } AssertHereDocumentEqual( wanted.HereDocument, - observed.HereDocument, + (observed as HereDocumentRedirectAnalysis)?.Document, $"{path}[{index}].hereDocument"); } } @@ -592,19 +587,56 @@ private static void AssertValueDomainEqual( string path) { var expectedValues = expected.Values ?? new List(); - if (expected.Kind != actual.Kind || - !expectedValues.SequenceEqual(actual.Values) || - expected.Pattern != actual.Pattern || - expected.CoveringDirectory != actual.CoveringDirectory) + var actualKind = ValueDomainKind(actual); + var actualValues = ValueDomainValues(actual); + var actualPattern = (actual as ShellValueDomain.PathPattern)?.Pattern; + var actualCoveringDirectory = + (actual as ShellValueDomain.PathPattern)?.CoveringDirectory; + if (expected.Kind != actualKind || + !expectedValues.SequenceEqual(actualValues) || + expected.Pattern != actualPattern || + expected.CoveringDirectory != actualCoveringDirectory) { throw new XunitException( $"{path}: expected={expected.Kind}[{string.Join(",", expectedValues)}] " + $"pattern={expected.Pattern}, covering={expected.CoveringDirectory}; " - + $"actual={actual.Kind}[{string.Join(",", actual.Values)}] " - + $"pattern={actual.Pattern}, covering={actual.CoveringDirectory}"); + + $"actual={actualKind}[{string.Join(",", actualValues)}] " + + $"pattern={actualPattern}, covering={actualCoveringDirectory}"); } } + private static string ValueDomainKind(ShellValueDomain domain) => domain switch + { + ShellValueDomain.Unknown => "Unknown", + ShellValueDomain.Exact => "Exact", + ShellValueDomain.FiniteSet => "FiniteSet", + ShellValueDomain.PathPattern => "Pattern", + _ => "Unknown", + }; + + private static IReadOnlyList ValueDomainValues(ShellValueDomain domain) => + domain switch + { + ShellValueDomain.Exact exact => new[] { exact.Value }, + ShellValueDomain.FiniteSet finiteSet => finiteSet.Values, + _ => Array.Empty(), + }; + + private static int? GetTargetDescriptor(RedirectAnalysis analysis) => analysis switch + { + DescriptorDuplicateRedirectAnalysis duplicate => duplicate.TargetDescriptor, + DescriptorMoveRedirectAnalysis move => move.TargetDescriptor, + _ => null, + }; + + private static ShellValueDomain GetRedirectValue(RedirectAnalysis analysis) => + analysis switch + { + FileRedirectAnalysis file => file.Target, + HereStringRedirectAnalysis hereString => hereString.Data, + _ => new ShellValueDomain.Unknown(), + }; + private static void AppendSyntax( ShellSyntaxNode node, int? parentIndex, @@ -615,7 +647,7 @@ private static void AppendSyntax( List nodes, bool isRootBlock = false) { - if (node.Kind == ShellSyntaxKind.Unknown) + if (SyntaxKind(node) == "Unknown") { throw new XunitException("Cannot flatten an unknown syntax node into corpus expectations"); } @@ -626,7 +658,7 @@ private static void AppendSyntax( int? clauseIndex = clause is null ? null : FindClauseIndex(clauses, clause); var currentIndex = nodes.Count; nodes.Add(new ActualSyntaxNode( - node.Kind, + SyntaxKind(node), parentIndex, region, childIndex, @@ -635,15 +667,15 @@ private static void AppendSyntax( clauseIndex, (node as GroupSyntax)?.GroupKind, listOperator, - forEachNode?.Binding.Name, - forEachNode?.Binding.Source.Raw, - forEachNode?.Binding.Source.SourceStart, - forEachNode?.Binding.Source.SourceLength, + forEachNode?.BindingName, + forEachNode?.BindingSource.Raw, + forEachNode?.BindingSource.SourceStart, + forEachNode?.BindingSource.SourceLength, forEachNode?.Iterable.Raw, forEachNode?.Iterable.SourceStart, forEachNode?.Iterable.SourceLength, executionRegion?.Origin, - executionRegion?.HostClauseElementIndex, + FindElementIndex(clauses, executionRegion?.HostArgument), executionRegion?.Phase, executionRegion?.Timing, executionRegion?.Cardinality, @@ -750,68 +782,6 @@ private static void AppendSyntax( clauses, nodes); break; - case ConditionLoopSyntax loop: - AppendSyntax( - loop.Condition, - currentIndex, - CommandAncestryRegion.Condition, - childIndex: null, - listOperator: null, - clauses, - nodes); - AppendSyntax( - loop.Body, - currentIndex, - CommandAncestryRegion.LoopBody, - childIndex: null, - listOperator: null, - clauses, - nodes); - break; - case ConditionalSyntax conditional: - for (var index = 0; index < conditional.Branches.Count; index++) - { - AppendSyntax( - conditional.Branches[index], - currentIndex, - CommandAncestryRegion.Branch, - index, - listOperator: null, - clauses, - nodes); - } - - if (conditional.Else is not null) - { - AppendSyntax( - conditional.Else, - currentIndex, - CommandAncestryRegion.Branch, - conditional.Branches.Count, - listOperator: null, - clauses, - nodes); - } - - break; - case ConditionalBranchSyntax branch: - AppendSyntax( - branch.Condition, - currentIndex, - CommandAncestryRegion.Condition, - childIndex: null, - listOperator: null, - clauses, - nodes); - AppendSyntax( - branch.Body, - currentIndex, - CommandAncestryRegion.Branch, - childIndex: null, - listOperator: null, - clauses, - nodes); - break; case CommandSubstitutionSyntax substitution: AppendSyntax( substitution.Body, @@ -851,6 +821,66 @@ private static int FindClauseIndex(IReadOnlyList clauses, Clause clause) return -1; } + private static int FindArgumentIndex(Clause clause, Arg argument) + { + for (var index = 0; index < clause.Args.Count; index++) + { + if (ReferenceEquals(clause.Args[index], argument)) + { + return index; + } + } + + return -1; + } + + private static int FindElementIndex(Clause clause, ClauseElement element) + { + for (var index = 0; index < clause.Elements.Count; index++) + { + if (ReferenceEquals(clause.Elements[index], element)) + { + return index; + } + } + + return -1; + } + + private static int? FindElementIndex( + IReadOnlyList clauses, + ClauseElement? element) + { + if (element is null) + { + return null; + } + + for (var clauseIndex = 0; clauseIndex < clauses.Count; clauseIndex++) + { + var index = FindElementIndex(clauses[clauseIndex], element); + if (index >= 0) + { + return index; + } + } + + return -1; + } + + private static string SyntaxKind(ShellSyntaxNode node) => node switch + { + ShellBlockSyntax => "Block", + SimpleCommandSyntax => "SimpleCommand", + PipelineSyntax => "Pipeline", + CommandListSyntax => "CommandList", + GroupSyntax => "Group", + ForEachSyntax => "ForEach", + CommandSubstitutionSyntax => "CommandSubstitution", + ExecutionRegionSyntax => "ExecutionRegion", + _ => "Unknown", + }; + private static string Summarize(ExpectedSyntaxNode node) => $"{{kind={node.Kind}, parent={node.ParentIndex}, region={node.Region}, " + $"child={node.ChildIndex}, span={node.SourceStart}:{node.SourceLength}, " @@ -870,7 +900,7 @@ private static string Summarize(ActualSyntaxNode node) => + $"hostElement={node.HostClauseElementIndex}}}"; private sealed record ActualSyntaxNode( - ShellSyntaxKind Kind, + string Kind, int? ParentIndex, CommandAncestryRegion Region, int? ChildIndex, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/CorpusRunnerTests.cs b/tests/ShellSyntaxTree.Tests/Corpus/CorpusRunnerTests.cs index 08ded7f..b17cf34 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/CorpusRunnerTests.cs +++ b/tests/ShellSyntaxTree.Tests/Corpus/CorpusRunnerTests.cs @@ -91,6 +91,7 @@ public void Executable_corpus_shape_preserves_execution_region_facts() new ExecutionRegionSyntax { Origin = ExecutionRegionOrigin.CommandArgument, + HostArgument = hostClause.Elements[1], HostClauseElementIndex = 1, Phase = ExecutionRegionPhase.Process, Timing = ExecutionRegionTiming.Synchronous, @@ -129,10 +130,10 @@ public void Executable_corpus_shape_preserves_execution_region_facts() }, Syntax = new List { - new() { Kind = ShellSyntaxKind.Block }, + new() { Kind = "Block" }, new() { - Kind = ShellSyntaxKind.SimpleCommand, + Kind = "SimpleCommand", ParentIndex = 0, Region = CommandAncestryRegion.Root, ChildIndex = 0, @@ -140,7 +141,7 @@ public void Executable_corpus_shape_preserves_execution_region_facts() }, new() { - Kind = ShellSyntaxKind.ExecutionRegion, + Kind = "ExecutionRegion", ParentIndex = 1, Region = CommandAncestryRegion.ExecutionRegion, ChildIndex = 0, @@ -152,14 +153,14 @@ public void Executable_corpus_shape_preserves_execution_region_facts() }, new() { - Kind = ShellSyntaxKind.Block, + Kind = "Block", ParentIndex = 2, Region = CommandAncestryRegion.ExecutionRegion, ChildIndex = 0, }, new() { - Kind = ShellSyntaxKind.SimpleCommand, + Kind = "SimpleCommand", ParentIndex = 3, Region = CommandAncestryRegion.Statement, ChildIndex = 0, @@ -210,10 +211,10 @@ static ExpectedParsedCommand CreateExpectation( { var syntax = new List { - new() { Kind = ShellSyntaxKind.Block }, + new() { Kind = "Block" }, new() { - Kind = ShellSyntaxKind.SimpleCommand, + Kind = "SimpleCommand", ParentIndex = 0, Region = CommandAncestryRegion.Root, ChildIndex = 0, @@ -224,7 +225,7 @@ static ExpectedParsedCommand CreateExpectation( { syntax.Add(new ExpectedSyntaxNode { - Kind = ShellSyntaxKind.ExecutionRegion, + Kind = "ExecutionRegion", ParentIndex = 1, Region = CommandAncestryRegion.ExecutionRegion, ChildIndex = index, @@ -999,7 +1000,7 @@ public sealed record ExpectedParsedCommand public sealed record ExpectedSyntaxNode { - public ShellSyntaxKind Kind { get; init; } + public string Kind { get; init; } = ""; public int? ParentIndex { get; init; } @@ -1052,7 +1053,7 @@ public sealed record ExpectedCommandOccurrence public List? Ancestry { get; init; } - public List? EffectiveArguments { get; init; } + public List? Arguments { get; init; } public ExpectedValueDomain? WorkingDirectory { get; init; } @@ -1063,20 +1064,20 @@ public sealed record ExpectedRedirectAnalysis { public int RedirectIndex { get; init; } = -1; - public RedirectSourceKind SourceKind { get; init; } + public string Kind { get; init; } = ""; + + public string SourceKind { get; init; } = ""; public int? SourceDescriptor { get; init; } - public RedirectOperation Operation { get; init; } + public FileRedirectMode? FileMode { get; init; } public int? TargetDescriptor { get; init; } - public ExpectedValueDomain Target { get; init; } = new(); + public ExpectedValueDomain? Value { get; init; } public ExpectedHereDocumentAnalysis? HereDocument { get; init; } - public bool IsPathRelevant { get; init; } - public bool IsComplete { get; init; } } @@ -1102,8 +1103,10 @@ public sealed record ExpectedSourceFragment public int? SourceLength { get; init; } } -public sealed record ExpectedEffectiveArgument +public sealed record ExpectedAnalyzedArgument { + public int ClauseArgumentIndex { get; init; } = -1; + public int ClauseElementIndex { get; init; } = -1; public ExpectedValueDomain Value { get; init; } = new(); @@ -1111,7 +1114,7 @@ public sealed record ExpectedEffectiveArgument public sealed record ExpectedValueDomain { - public ShellValueDomainKind Kind { get; init; } + public string Kind { get; init; } = ""; public List? Values { get; init; } @@ -1122,7 +1125,7 @@ public sealed record ExpectedValueDomain public sealed record ExpectedCommandAncestryFrame { - public ShellSyntaxKind AncestorKind { get; init; } + public string AncestorKind { get; init; } = ""; public CommandAncestryRegion Region { get; init; } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/116_redirect_fd_dup_stderr_to_stdout.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/116_redirect_fd_dup_stderr_to_stdout.json index a2c6f25..73c8c2f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/116_redirect_fd_dup_stderr_to_stdout.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/116_redirect_fd_dup_stderr_to_stdout.json @@ -1,18 +1,22 @@ { - "name": "Redirect fd-dup: cmd 2>&1", - "input": "cmd 2>&1", + "name": "Redirect fd-dup: cmd 2\u003E\u00261", + "input": "cmd 2\u003E\u00261", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["cmd"], + "verb": [ + "cmd" + ], "args": [], "redirects": [ - { "direction": "ErrOut", "target": "&1", "isDynamicSkip": true } - ], - "isSubshell": false, - "isCommandStringWrapped": false + { + "direction": "ErrOut", + "target": "\u00261", + "isDynamicSkip": true + } + ] } ], "commands": [ @@ -21,18 +25,31 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 8 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 8 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorDuplicateRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "DescriptorDuplicate", - "targetDescriptor": 1, - "target": { "kind": "Unknown" }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/118_redirect_fd_close.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/118_redirect_fd_close.json index e03bbce..c36a4a4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/118_redirect_fd_close.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/118_redirect_fd_close.json @@ -1,18 +1,22 @@ { - "name": "Redirect fd-close: cmd 2>&-", - "input": "cmd 2>&-", + "name": "Redirect fd-close: cmd 2\u003E\u0026-", + "input": "cmd 2\u003E\u0026-", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["cmd"], + "verb": [ + "cmd" + ], "args": [], "redirects": [ - { "direction": "ErrOut", "target": "&-", "isDynamicSkip": true } - ], - "isSubshell": false, - "isCommandStringWrapped": false + { + "direction": "ErrOut", + "target": "\u0026-", + "isDynamicSkip": true + } + ] } ], "commands": [ @@ -21,16 +25,29 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 8 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 8 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorCloseRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "DescriptorClose", - "target": { "kind": "Unknown" }, - "isPathRelevant": false, "isComplete": true } ] diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/166_curl_data_mixed_literal_dynamic.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/166_curl_data_mixed_literal_dynamic.json index 4a30323..fd7b24b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/166_curl_data_mixed_literal_dynamic.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/166_curl_data_mixed_literal_dynamic.json @@ -1,6 +1,6 @@ { "name": "Curl data mixed literal dynamic", - "input": "curl --data='@$HOME'\".json\" https://example.invalid/api", + "input": "curl --data=\u0027@$HOME\u0027\u0022.json\u0022 https://example.invalid/api", "expected": { "isUnparseable": false, "clauses": [ @@ -18,7 +18,7 @@ "isFlag": true }, { - "raw": "'@$HOME'\".json\"", + "raw": "\u0027@$HOME\u0027\u0022.json\u0022", "kind": "Literal", "isPath": true, "resolved": "/work/$HOME.json", @@ -46,7 +46,7 @@ "isPath": false }, { - "raw": "--data='@$HOME'\".json\"", + "raw": "--data=\u0027@$HOME\u0027\u0022.json\u0022", "value": "--data=@$HOME.json", "role": "Argument", "sourceStart": 5, @@ -109,8 +109,21 @@ "sourceLength": 55 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--data" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 1, "value": { "kind": "Exact", @@ -120,6 +133,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid/api" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/168_escaped_home_literal_path.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/168_escaped_home_literal_path.json index d1c2738..ec60a14 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/168_escaped_home_literal_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/168_escaped_home_literal_path.json @@ -84,8 +84,9 @@ "sourceLength": 10 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/170_adjacent_escaped_redirect_target.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/170_adjacent_escaped_redirect_target.json index 23efa8a..c4f1c6d 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/170_adjacent_escaped_redirect_target.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/170_adjacent_escaped_redirect_target.json @@ -1,6 +1,6 @@ { "name": "Adjacent escaped redirect fragments form one literal target", - "input": "echo ok > \\$HOME\".txt\"", + "input": "echo ok \u003E \\$HOME\u0022.txt\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -42,7 +42,7 @@ "isPath": false }, { - "raw": "> \\$HOME\".txt\"", + "raw": "\u003E \\$HOME\u0022.txt\u0022", "value": "$HOME.txt", "role": "Redirect", "sourceStart": 8, @@ -94,7 +94,7 @@ "sourceLength": 22 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -106,20 +106,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "/work/$HOME.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/171_runtime_special_parameter_path.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/171_runtime_special_parameter_path.json index c846915..b26300d 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/171_runtime_special_parameter_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/171_runtime_special_parameter_path.json @@ -1,6 +1,6 @@ { "name": "Runtime special parameter path fails closed", - "input": "cat \"$?\"", + "input": "cat \u0022$?\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"$?\"", + "raw": "\u0022$?\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -32,7 +32,7 @@ "isPath": false }, { - "raw": "\"$?\"", + "raw": "\u0022$?\u0022", "value": "$?", "role": "Argument", "sourceStart": 4, @@ -83,8 +83,9 @@ "sourceLength": 8 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/172_quoted_wildcard_redirect.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/172_quoted_wildcard_redirect.json index 7c389c0..e9f4df8 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/172_quoted_wildcard_redirect.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/172_quoted_wildcard_redirect.json @@ -1,6 +1,6 @@ { "name": "Quoted wildcard redirect is one literal target", - "input": "echo ok > \"*.txt\"", + "input": "echo ok \u003E \u0022*.txt\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -42,7 +42,7 @@ "isPath": false }, { - "raw": "> \"*.txt\"", + "raw": "\u003E \u0022*.txt\u0022", "value": "*.txt", "role": "Redirect", "sourceStart": 8, @@ -94,7 +94,7 @@ "sourceLength": 17 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -106,20 +106,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "/work/*.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/173_unquoted_wildcard_redirect.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/173_unquoted_wildcard_redirect.json index d3cd03c..0068322 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/173_unquoted_wildcard_redirect.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/173_unquoted_wildcard_redirect.json @@ -1,6 +1,6 @@ { "name": "Unquoted wildcard redirect fails closed", - "input": "echo ok > *.txt", + "input": "echo ok \u003E *.txt", "expected": { "isUnparseable": false, "clauses": [ @@ -42,7 +42,7 @@ "isPath": false }, { - "raw": "> *.txt", + "raw": "\u003E *.txt", "value": "*.txt", "role": "Redirect", "sourceStart": 8, @@ -93,7 +93,7 @@ "sourceLength": 15 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -105,18 +105,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": false, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": false + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/183_escaped_open_brace_literal_path.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/183_escaped_open_brace_literal_path.json index 032714d..85977c1 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/183_escaped_open_brace_literal_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/183_escaped_open_brace_literal_path.json @@ -1,6 +1,6 @@ { "name": "Escaped interpolation start remains a literal path", - "input": "cat \"\\${HOME\"", + "input": "cat \u0022\\${HOME\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"\\${HOME\"", + "raw": "\u0022\\${HOME\u0022", "kind": "Literal", "isPath": true, "resolved": "/work/${HOME", @@ -32,7 +32,7 @@ "isPath": false }, { - "raw": "\"\\${HOME\"", + "raw": "\u0022\\${HOME\u0022", "value": "${HOME", "role": "Argument", "sourceStart": 4, @@ -84,8 +84,9 @@ "sourceLength": 13 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/185_provider_looking_unquoted_path.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/185_provider_looking_unquoted_path.json index b49205a..7ca94a1 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/185_provider_looking_unquoted_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/185_provider_looking_unquoted_path.json @@ -84,8 +84,9 @@ "sourceLength": 21 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/186_provider_looking_quoted_path.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/186_provider_looking_quoted_path.json index ec049d6..980fce0 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/186_provider_looking_quoted_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/186_provider_looking_quoted_path.json @@ -1,6 +1,6 @@ { "name": "Bash provider-looking quoted path remains literal", - "input": "cat \"filesystem::/safe\"", + "input": "cat \u0022filesystem::/safe\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"filesystem::/safe\"", + "raw": "\u0022filesystem::/safe\u0022", "kind": "Literal", "isPath": true, "resolved": "/work/filesystem::/safe", @@ -32,7 +32,7 @@ "isPath": false }, { - "raw": "\"filesystem::/safe\"", + "raw": "\u0022filesystem::/safe\u0022", "value": "filesystem::/safe", "role": "Argument", "sourceStart": 4, @@ -84,8 +84,9 @@ "sourceLength": 23 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/200_v03_expanding_heredoc_substitution.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/200_v03_expanding_heredoc_substitution.json index 331d75e..a2df8be 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/200_v03_expanding_heredoc_substitution.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/200_v03_expanding_heredoc_substitution.json @@ -1,6 +1,6 @@ { "name": "v0.3 expanding heredoc command substitution", - "input": "cat <", - "isDynamicSkip": false + "target": "\u003C\u003CEOF\u003E" } ] } @@ -111,17 +110,46 @@ "sourceStart": 12, "sourceLength": 2 } - ] + ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } }, { "clauseIndex": 1, "immediateRole": "Ordinary", "isComplete": true, + "ancestry": [ + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 19 + } + ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "HereDocumentRedirectAnalysis", "sourceKind": "Default", - "operation": "HereDocument", + "sourceDescriptor": null, + "isComplete": true, "hereDocument": { "delimiter": { "raw": "EOF", @@ -136,18 +164,7 @@ "expansionMode": "Expand", "stripLeadingTabs": false, "isComplete": true - }, - "isPathRelevant": false, - "isComplete": true - } - ], - "ancestry": [ - { - "ancestorKind": "Block", - "region": "Root", - "childIndex": 0, - "sourceStart": 0, - "sourceLength": 19 + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/201_v03_literal_heredoc_substitution_spelling.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/201_v03_literal_heredoc_substitution_spelling.json index 4f295e8..f937957 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/201_v03_literal_heredoc_substitution_spelling.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/201_v03_literal_heredoc_substitution_spelling.json @@ -1,6 +1,6 @@ { "name": "v0.3 quoted heredoc keeps substitution spelling literal", - "input": "cat <", - "isDynamicSkip": false + "target": "\u003C\u003CEOF\u003E" } ] } @@ -48,14 +47,34 @@ "clauseIndex": 0, "immediateRole": "Ordinary", "isComplete": true, + "ancestry": [ + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 21 + } + ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "HereDocumentRedirectAnalysis", "sourceKind": "Default", - "operation": "HereDocument", + "sourceDescriptor": null, + "isComplete": true, "hereDocument": { "delimiter": { - "raw": "E\"OF\"", + "raw": "E\u0022OF\u0022", "sourceStart": 6, "sourceLength": 5 }, @@ -67,18 +86,7 @@ "expansionMode": "Literal", "stripLeadingTabs": false, "isComplete": true - }, - "isPathRelevant": false, - "isComplete": true - } - ], - "ancestry": [ - { - "ancestorKind": "Block", - "region": "Root", - "childIndex": 0, - "sourceStart": 0, - "sourceLength": 21 + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/202_v03_tab_stripping_heredoc_substitution.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/202_v03_tab_stripping_heredoc_substitution.json index deedb36..1862937 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/202_v03_tab_stripping_heredoc_substitution.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/202_v03_tab_stripping_heredoc_substitution.json @@ -1,6 +1,6 @@ { "name": "v0.3 tab-stripping heredoc command substitution", - "input": "cat <<-EOF\n\t$(id)\n\tEOF", + "input": "cat \u003C\u003C-EOF\n\t$(id)\n\tEOF", "expected": { "isUnparseable": false, "clauses": [ @@ -21,8 +21,7 @@ "redirects": [ { "direction": "In", - "target": "<", - "isDynamicSkip": false + "target": "\u003C\u003CEOF\u003E" } ] } @@ -111,17 +110,46 @@ "sourceStart": 14, "sourceLength": 2 } - ] + ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } }, { "clauseIndex": 1, "immediateRole": "Ordinary", "isComplete": true, + "ancestry": [ + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 22 + } + ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "HereDocumentRedirectAnalysis", "sourceKind": "Default", - "operation": "HereDocument", + "sourceDescriptor": null, + "isComplete": true, "hereDocument": { "delimiter": { "raw": "EOF", @@ -136,22 +164,11 @@ "expansionMode": "Expand", "stripLeadingTabs": true, "isComplete": true - }, - "isPathRelevant": false, - "isComplete": true - } - ], - "ancestry": [ - { - "ancestorKind": "Block", - "region": "Root", - "childIndex": 0, - "sourceStart": 0, - "sourceLength": 22 + } } ] } ] }, - "notes": "The <<- delimiter accepts authored leading tabs without hiding an expanding-body substitution." + "notes": "The \u003C\u003C- delimiter accepts authored leading tabs without hiding an expanding-body substitution." } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/203_v03_heredoc_multiple_substitutions.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/203_v03_heredoc_multiple_substitutions.json index 6aec317..fd43a6a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/203_v03_heredoc_multiple_substitutions.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/203_v03_heredoc_multiple_substitutions.json @@ -1,6 +1,6 @@ { "name": "v0.3 heredoc sibling command substitutions", - "input": "cat <", - "isDynamicSkip": false + "target": "\u003C\u003CEOF\u003E" } ] } @@ -152,7 +151,16 @@ "sourceStart": 12, "sourceLength": 5 } - ] + ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } }, { "clauseIndex": 1, @@ -180,17 +188,46 @@ "sourceStart": 21, "sourceLength": 6 } - ] + ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } }, { "clauseIndex": 2, "immediateRole": "Ordinary", "isComplete": true, + "ancestry": [ + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 32 + } + ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "HereDocumentRedirectAnalysis", "sourceKind": "Default", - "operation": "HereDocument", + "sourceDescriptor": null, + "isComplete": true, "hereDocument": { "delimiter": { "raw": "EOF", @@ -205,18 +242,7 @@ "expansionMode": "Expand", "stripLeadingTabs": false, "isComplete": true - }, - "isPathRelevant": false, - "isComplete": true - } - ], - "ancestry": [ - { - "ancestorKind": "Block", - "region": "Root", - "childIndex": 0, - "sourceStart": 0, - "sourceLength": 32 + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/205_v03_for_literal_finite.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/205_v03_for_literal_finite.json index 588f16e..04282dc 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/205_v03_for_literal_finite.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/205_v03_for_literal_finite.json @@ -1,25 +1,96 @@ { "name": "v0.3 Bash for-in literal finite binding", - "input": "for f in a.txt b.txt; do rm -- \"$f\"; done", + "input": "for f in a.txt b.txt; do rm -- \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["rm"], + "verb": [ + "rm" + ], "args": [ - { "raw": "--", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": true }, - { "raw": "\"$f\"", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "--", + "kind": "Literal", + "isPath": false, + "resolved": "__NULL__", + "isFlag": true + }, + { + "raw": "\u0022$f\u0022", + "kind": "DynamicSkip", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [] } ], "syntax": [ - { "kind": "Block", "parentIndex": null, "region": "Unknown", "childIndex": null, "sourceStart": 0, "sourceLength": 41, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "ForEach", "parentIndex": 0, "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 41, "clauseIndex": null, "groupKind": null, "listOperator": null, "bindingName": "f", "bindingRaw": "f", "bindingSourceStart": 4, "bindingSourceLength": 1, "iterableRaw": "a.txt b.txt", "iterableSourceStart": 9, "iterableSourceLength": 11 }, - { "kind": "Block", "parentIndex": 1, "region": "Iterator", "childIndex": null, "sourceStart": 9, "sourceLength": 11, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "Block", "parentIndex": 1, "region": "LoopBody", "childIndex": null, "sourceStart": 24, "sourceLength": 13, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "SimpleCommand", "parentIndex": 3, "region": "Statement", "childIndex": 0, "sourceStart": 25, "sourceLength": 10, "clauseIndex": 0, "groupKind": null, "listOperator": null } + { + "kind": "Block", + "parentIndex": null, + "region": "Unknown", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 41, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "ForEach", + "parentIndex": 0, + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 41, + "clauseIndex": null, + "groupKind": null, + "listOperator": null, + "bindingName": "f", + "bindingRaw": "f", + "bindingSourceStart": 4, + "bindingSourceLength": 1, + "iterableRaw": "a.txt b.txt", + "iterableSourceStart": 9, + "iterableSourceLength": 11 + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "Iterator", + "childIndex": null, + "sourceStart": 9, + "sourceLength": 11, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "LoopBody", + "childIndex": null, + "sourceStart": 24, + "sourceLength": 13, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 3, + "region": "Statement", + "childIndex": 0, + "sourceStart": 25, + "sourceLength": 10, + "clauseIndex": 0, + "groupKind": null, + "listOperator": null + } ], "commands": [ { @@ -27,14 +98,63 @@ "immediateRole": "LoopBody", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 41 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 41 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 24, "sourceLength": 13 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 41 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 41 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 24, + "sourceLength": 13 + } ], - "effectiveArguments": [ - { "clauseElementIndex": 2, "value": { "kind": "FiniteSet", "values": ["a.txt", "b.txt"], "pattern": null, "coveringDirectory": null } } + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "FiniteSet", + "values": [ + "a.txt", + "b.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } ], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/206_v03_for_iterator_substitution.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/206_v03_for_iterator_substitution.json index 804770a..d5a3dbb 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/206_v03_for_iterator_substitution.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/206_v03_for_iterator_substitution.json @@ -1,38 +1,159 @@ { "name": "v0.3 Bash for-in iterator substitution", - "input": "for f in $(find /tmp -type f); do rm -- \"$f\"; done", + "input": "for f in $(find /tmp -type f); do rm -- \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["find"], + "verb": [ + "find" + ], "args": [ - { "raw": "/tmp", "kind": "Literal", "isPath": true, "resolved": "/tmp", "isFlag": false }, - { "raw": "-type", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": true }, - { "raw": "f", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "/tmp", + "kind": "Literal", + "isPath": true, + "resolved": "/tmp", + "isFlag": false + }, + { + "raw": "-type", + "kind": "Literal", + "isPath": false, + "resolved": "__NULL__", + "isFlag": true + }, + { + "raw": "f", + "kind": "Literal", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [] }, { "operator": "None", - "verb": ["rm"], + "verb": [ + "rm" + ], "args": [ - { "raw": "--", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": true }, - { "raw": "\"$f\"", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "--", + "kind": "Literal", + "isPath": false, + "resolved": "__NULL__", + "isFlag": true + }, + { + "raw": "\u0022$f\u0022", + "kind": "DynamicSkip", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [] } ], "syntax": [ - { "kind": "Block", "parentIndex": null, "region": "Unknown", "childIndex": null, "sourceStart": 0, "sourceLength": 50, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "ForEach", "parentIndex": 0, "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 50, "clauseIndex": null, "groupKind": null, "listOperator": null, "bindingName": "f", "bindingRaw": "f", "bindingSourceStart": 4, "bindingSourceLength": 1, "iterableRaw": "$(find /tmp -type f)", "iterableSourceStart": 9, "iterableSourceLength": 20 }, - { "kind": "Block", "parentIndex": 1, "region": "Iterator", "childIndex": null, "sourceStart": 9, "sourceLength": 20, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "CommandSubstitution", "parentIndex": 2, "region": "Statement", "childIndex": 0, "sourceStart": 9, "sourceLength": 20, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "Block", "parentIndex": 3, "region": "Substitution", "childIndex": 0, "sourceStart": 11, "sourceLength": 17, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "SimpleCommand", "parentIndex": 4, "region": "Statement", "childIndex": 0, "sourceStart": 11, "sourceLength": 17, "clauseIndex": 0, "groupKind": null, "listOperator": null }, - { "kind": "Block", "parentIndex": 1, "region": "LoopBody", "childIndex": null, "sourceStart": 33, "sourceLength": 13, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "SimpleCommand", "parentIndex": 6, "region": "Statement", "childIndex": 0, "sourceStart": 34, "sourceLength": 10, "clauseIndex": 1, "groupKind": null, "listOperator": null } + { + "kind": "Block", + "parentIndex": null, + "region": "Unknown", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 50, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "ForEach", + "parentIndex": 0, + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 50, + "clauseIndex": null, + "groupKind": null, + "listOperator": null, + "bindingName": "f", + "bindingRaw": "f", + "bindingSourceStart": 4, + "bindingSourceLength": 1, + "iterableRaw": "$(find /tmp -type f)", + "iterableSourceStart": 9, + "iterableSourceLength": 20 + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "Iterator", + "childIndex": null, + "sourceStart": 9, + "sourceLength": 20, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "CommandSubstitution", + "parentIndex": 2, + "region": "Statement", + "childIndex": 0, + "sourceStart": 9, + "sourceLength": 20, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "Block", + "parentIndex": 3, + "region": "Substitution", + "childIndex": 0, + "sourceStart": 11, + "sourceLength": 17, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 4, + "region": "Statement", + "childIndex": 0, + "sourceStart": 11, + "sourceLength": 17, + "clauseIndex": 0, + "groupKind": null, + "listOperator": null + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "LoopBody", + "childIndex": null, + "sourceStart": 33, + "sourceLength": 13, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 6, + "region": "Statement", + "childIndex": 0, + "sourceStart": 34, + "sourceLength": 10, + "clauseIndex": 1, + "groupKind": null, + "listOperator": null + } ], "commands": [ { @@ -40,28 +161,148 @@ "immediateRole": "Substitution", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 50 }, - { "ancestorKind": "ForEach", "region": "Iterator", "childIndex": null, "sourceStart": 0, "sourceLength": 50 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 9, "sourceLength": 20 }, - { "ancestorKind": "CommandSubstitution", "region": "Substitution", "childIndex": 0, "sourceStart": 9, "sourceLength": 20 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 11, "sourceLength": 17 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 50 + }, + { + "ancestorKind": "ForEach", + "region": "Iterator", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 50 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 9, + "sourceLength": 20 + }, + { + "ancestorKind": "CommandSubstitution", + "region": "Substitution", + "childIndex": 0, + "sourceStart": 9, + "sourceLength": 20 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 11, + "sourceLength": 17 + } + ], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "/tmp" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "-type" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "f" + ], + "pattern": null, + "coveringDirectory": null + } + } ], - "effectiveArguments": [], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } }, { "clauseIndex": 1, "immediateRole": "LoopBody", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 50 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 50 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 33, "sourceLength": 13 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 50 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 50 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 33, + "sourceLength": 13 + } ], - "effectiveArguments": [ - { "clauseElementIndex": 2, "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null } } + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } ], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/207_v03_for_static_pattern.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/207_v03_for_static_pattern.json index 54dc844..50196d4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/207_v03_for_static_pattern.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/207_v03_for_static_pattern.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash for-in static path pattern", - "input": "for f in /tmp/*.txt; do rm -- \"$f\"; done", + "input": "for f in /tmp/*.txt; do rm -- \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ @@ -18,7 +18,7 @@ "isFlag": true }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -120,8 +120,21 @@ "sourceLength": 13 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Pattern", @@ -133,7 +146,9 @@ ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/208_v03_for_dynamic_root.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/208_v03_for_dynamic_root.json index 6b9c531..f733ed8 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/208_v03_for_dynamic_root.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/208_v03_for_dynamic_root.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash for-in dynamic glob root", - "input": "for f in \"$DIR\"/*.txt; do rm -- \"$f\"; done", + "input": "for f in \u0022$DIR\u0022/*.txt; do rm -- \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ @@ -18,7 +18,7 @@ "isFlag": true }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -54,7 +54,7 @@ "bindingRaw": "f", "bindingSourceStart": 4, "bindingSourceLength": 1, - "iterableRaw": "\"$DIR\"/*.txt", + "iterableRaw": "\u0022$DIR\u0022/*.txt", "iterableSourceStart": 9, "iterableSourceLength": 12 }, @@ -120,8 +120,21 @@ "sourceLength": 13 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", @@ -133,7 +146,9 @@ ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/209_v03_for_option_injection.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/209_v03_for_option_injection.json index 080be38..5555b18 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/209_v03_for_option_injection.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/209_v03_for_option_injection.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash for-in option-shaped candidates", - "input": "for f in -rf safe; do rm \"$f\" target; done", + "input": "for f in -rf safe; do rm \u0022$f\u0022 target; done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -120,8 +120,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", @@ -132,11 +133,25 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "target" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/210_v03_for_nested_correlation.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/210_v03_for_nested_correlation.json index a203e63..c9b6ee2 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/210_v03_for_nested_correlation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/210_v03_for_nested_correlation.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash nested for-in correlation", - "input": "for d in a b; do for f in x y; do echo \"$d/$f/$d\"; done; done", + "input": "for d in a b; do for f in x y; do echo \u0022$d/$f/$d\u0022; done; done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"$d/$f/$d\"", + "raw": "\u0022$d/$f/$d\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -167,8 +167,9 @@ "sourceLength": 18 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", @@ -185,7 +186,9 @@ ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/211_v03_for_pipeline_ancestry.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/211_v03_for_pipeline_ancestry.json index 0699e46..b1c5495 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/211_v03_for_pipeline_ancestry.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/211_v03_for_pipeline_ancestry.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash for-in body pipeline ancestry", - "input": "for f in a b; do find \"$f\" | xargs rm --; done", + "input": "for f in a b; do find \u0022$f\u0022 | xargs rm --; done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -159,8 +159,9 @@ "sourceLength": 23 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", @@ -175,7 +176,9 @@ ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } @@ -214,10 +217,25 @@ "sourceLength": 23 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "--" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/213_v03_for_candidate_cap_32.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/213_v03_for_candidate_cap_32.json index 5074a69..8707e44 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/213_v03_for_candidate_cap_32.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/213_v03_for_candidate_cap_32.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash for-in exact candidate cap", - "input": "for f in v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31; do printf %s \"$f\"; done", + "input": "for f in v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31; do printf %s \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ @@ -18,7 +18,7 @@ "isFlag": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", @@ -120,8 +120,21 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "FiniteSet", @@ -166,7 +179,9 @@ ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/214_v03_for_candidate_overflow_33.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/214_v03_for_candidate_overflow_33.json index 9e77d03..b90b57f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/214_v03_for_candidate_overflow_33.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/214_v03_for_candidate_overflow_33.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash for-in candidate overflow", - "input": "for f in v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32; do printf %s \"$f\"; done", + "input": "for f in v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32; do printf %s \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ @@ -18,7 +18,7 @@ "isFlag": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", @@ -120,8 +120,21 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", @@ -133,7 +146,9 @@ ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/215_v03_for_pattern_parent_traversal.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/215_v03_for_pattern_parent_traversal.json index 0209652..81563c9 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/215_v03_for_pattern_parent_traversal.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/215_v03_for_pattern_parent_traversal.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash for-in pattern parent traversal", - "input": "for f in /tmp/*/../../etc/passwd; do rm -- \"$f\"; done", + "input": "for f in /tmp/*/../../etc/passwd; do rm -- \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ @@ -18,7 +18,7 @@ "isFlag": true }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -120,8 +120,21 @@ "sourceLength": 13 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", @@ -133,7 +146,9 @@ ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/216_v03_for_dot_glob_parent_escape.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/216_v03_for_dot_glob_parent_escape.json index 6f7ca7e..f5dc422 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/216_v03_for_dot_glob_parent_escape.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/216_v03_for_dot_glob_parent_escape.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash for-in dot glob parent escape", - "input": "for f in /tmp/.[.]/etc/passwd; do rm -- \"$f\"; done", + "input": "for f in /tmp/.[.]/etc/passwd; do rm -- \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ @@ -18,7 +18,7 @@ "isFlag": true }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -120,8 +120,21 @@ "sourceLength": 13 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", @@ -133,7 +146,9 @@ ], "workingDirectory": { "kind": "Exact", - "values": ["/work"], + "values": [ + "/work" + ], "pattern": null, "coveringDirectory": null } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/239_v03_for_ordered_duplicate_post_state.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/239_v03_for_ordered_duplicate_post_state.json index afca31d..77b1fac 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/239_v03_for_ordered_duplicate_post_state.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/239_v03_for_ordered_duplicate_post_state.json @@ -1,23 +1,45 @@ { "name": "v0.3 Bash ordered duplicate loop state", - "input": "for f in a b a; do printf \"%s\" \"$f\"; done; echo \"$f\"", + "input": "for f in a b a; do printf \u0022%s\u0022 \u0022$f\u0022; done; echo \u0022$f\u0022", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["printf"], + "verb": [ + "printf" + ], "args": [ - { "raw": "\"%s\"", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, - { "raw": "\"$f\"", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "\u0022%s\u0022", + "kind": "Literal", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + }, + { + "raw": "\u0022$f\u0022", + "kind": "EnvVar", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [] }, { "operator": "Sequence", - "verb": ["echo"], + "verb": [ + "echo" + ], "args": [ - { "raw": "\"$f\"", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "\u0022$f\u0022", + "kind": "EnvVar", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [] } @@ -28,28 +50,113 @@ "immediateRole": "LoopBody", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 52 }, - { "ancestorKind": "CommandList", "region": "Statement", "childIndex": 0, "sourceStart": 0, "sourceLength": 52 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 41 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 18, "sourceLength": 19 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 52 + }, + { + "ancestorKind": "CommandList", + "region": "Statement", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 52 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 41 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 18, + "sourceLength": 19 + } ], - "effectiveArguments": [ - { "clauseElementIndex": 2, "value": { "kind": "FiniteSet", "values": ["a", "b"], "pattern": null, "coveringDirectory": null } } + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "FiniteSet", + "values": [ + "a", + "b" + ], + "pattern": null, + "coveringDirectory": null + } + } ], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } }, { "clauseIndex": 1, "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 52 }, - { "ancestorKind": "CommandList", "region": "Statement", "childIndex": 1, "sourceStart": 0, "sourceLength": 52 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 52 + }, + { + "ancestorKind": "CommandList", + "region": "Statement", + "childIndex": 1, + "sourceStart": 0, + "sourceLength": 52 + } ], - "effectiveArguments": [ - { "clauseElementIndex": 1, "value": { "kind": "Exact", "values": ["a"], "pattern": null, "coveringDirectory": null } } + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "a" + ], + "pattern": null, + "coveringDirectory": null + } + } ], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/240_v03_for_empty_or_unreachable.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/240_v03_for_empty_or_unreachable.json index e1b7723..f8593ee 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/240_v03_for_empty_or_unreachable.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/240_v03_for_empty_or_unreachable.json @@ -6,18 +6,42 @@ "clauses": [ { "operator": "None", - "verb": ["false"], + "verb": [ + "false" + ], "args": [ - { "raw": "\u003Cdynamic-cwd\u003E", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", "isFlag": false, "isCwdAttribution": true } + { + "raw": "\u003Cdynamic-cwd\u003E", + "kind": "DynamicSkip", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false, + "isCwdAttribution": true + } ], "redirects": [] }, { "operator": "OrIf", - "verb": ["cat"], + "verb": [ + "cat" + ], "args": [ - { "raw": "relative.txt", "kind": "Literal", "isPath": true, "resolved": "__NULL__", "isFlag": false }, - { "raw": "\u003Cdynamic-cwd\u003E", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", "isFlag": false, "isCwdAttribution": true } + { + "raw": "relative.txt", + "kind": "Literal", + "isPath": true, + "resolved": "__NULL__", + "isFlag": false + }, + { + "raw": "\u003Cdynamic-cwd\u003E", + "kind": "DynamicSkip", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false, + "isCwdAttribution": true + } ], "redirects": [] } @@ -28,24 +52,83 @@ "immediateRole": "LoopBody", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 44 }, - { "ancestorKind": "CommandList", "region": "Statement", "childIndex": 0, "sourceStart": 0, "sourceLength": 44 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 24 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 12, "sourceLength": 8 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 44 + }, + { + "ancestorKind": "CommandList", + "region": "Statement", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 44 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 24 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 12, + "sourceLength": 8 + } ], - "effectiveArguments": [], - "workingDirectory": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null } + "arguments": [], + "workingDirectory": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } }, { "clauseIndex": 1, "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 44 }, - { "ancestorKind": "CommandList", "region": "Statement", "childIndex": 1, "sourceStart": 0, "sourceLength": 44 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 44 + }, + { + "ancestorKind": "CommandList", + "region": "Statement", + "childIndex": 1, + "sourceStart": 0, + "sourceLength": 44 + } + ], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "relative.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } ], - "effectiveArguments": [], - "workingDirectory": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/251_v03_for_multiline_redirect.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/251_v03_for_multiline_redirect.json index 26976f8..0070ed8 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/251_v03_for_multiline_redirect.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/251_v03_for_multiline_redirect.json @@ -1,37 +1,140 @@ { "name": "v0.3 Bash multiline for-in body with redirect", - "input": "for f in a b\ndo\nprintf '%s\\n' \"$f\" > out.txt\necho done\ndone", + "input": "for f in a b\ndo\nprintf \u0027%s\\n\u0027 \u0022$f\u0022 \u003E out.txt\necho done\ndone", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["printf"], + "verb": [ + "printf" + ], "args": [ - { "raw": "'%s\\n'", "kind": "Literal", "isPath": true, "resolved": "/work/%s/n", "isFlag": false }, - { "raw": "\"$f\"", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "\u0027%s\\n\u0027", + "kind": "Literal", + "isPath": true, + "resolved": "/work/%s/n", + "isFlag": false + }, + { + "raw": "\u0022$f\u0022", + "kind": "EnvVar", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [ - { "direction": "Out", "target": "/work/out.txt", "isDynamicSkip": false } + { + "direction": "Out", + "target": "/work/out.txt", + "isDynamicSkip": false + } ] }, { "operator": "Sequence", - "verb": ["echo"], + "verb": [ + "echo" + ], "args": [ - { "raw": "done", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "done", + "kind": "Literal", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [] } ], "syntax": [ - { "kind": "Block", "parentIndex": null, "region": "Unknown", "childIndex": null, "sourceStart": 0, "sourceLength": 59, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "ForEach", "parentIndex": 0, "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 59, "clauseIndex": null, "groupKind": null, "listOperator": null, "bindingName": "f", "bindingRaw": "f", "bindingSourceStart": 4, "bindingSourceLength": 1, "iterableRaw": "a b", "iterableSourceStart": 9, "iterableSourceLength": 3 }, - { "kind": "Block", "parentIndex": 1, "region": "Iterator", "childIndex": null, "sourceStart": 9, "sourceLength": 3, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "Block", "parentIndex": 1, "region": "LoopBody", "childIndex": null, "sourceStart": 15, "sourceLength": 40, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "CommandList", "parentIndex": 3, "region": "Statement", "childIndex": 0, "sourceStart": 16, "sourceLength": 38, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "SimpleCommand", "parentIndex": 4, "region": "Statement", "childIndex": 0, "sourceStart": 16, "sourceLength": 28, "clauseIndex": 0, "groupKind": null, "listOperator": "None" }, - { "kind": "SimpleCommand", "parentIndex": 4, "region": "Statement", "childIndex": 1, "sourceStart": 45, "sourceLength": 9, "clauseIndex": 1, "groupKind": null, "listOperator": "Sequence" } + { + "kind": "Block", + "parentIndex": null, + "region": "Unknown", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 59, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "ForEach", + "parentIndex": 0, + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 59, + "clauseIndex": null, + "groupKind": null, + "listOperator": null, + "bindingName": "f", + "bindingRaw": "f", + "bindingSourceStart": 4, + "bindingSourceLength": 1, + "iterableRaw": "a b", + "iterableSourceStart": 9, + "iterableSourceLength": 3 + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "Iterator", + "childIndex": null, + "sourceStart": 9, + "sourceLength": 3, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "LoopBody", + "childIndex": null, + "sourceStart": 15, + "sourceLength": 40, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "CommandList", + "parentIndex": 3, + "region": "Statement", + "childIndex": 0, + "sourceStart": 16, + "sourceLength": 38, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 4, + "region": "Statement", + "childIndex": 0, + "sourceStart": 16, + "sourceLength": 28, + "clauseIndex": 0, + "groupKind": null, + "listOperator": "None" + }, + { + "kind": "SimpleCommand", + "parentIndex": 4, + "region": "Statement", + "childIndex": 1, + "sourceStart": 45, + "sourceLength": 9, + "clauseIndex": 1, + "groupKind": null, + "listOperator": "Sequence" + } ], "commands": [ { @@ -39,25 +142,86 @@ "immediateRole": "LoopBody", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 59 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 59 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 15, "sourceLength": 40 }, - { "ancestorKind": "CommandList", "region": "Statement", "childIndex": 0, "sourceStart": 16, "sourceLength": 38 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 59 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 59 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 15, + "sourceLength": 40 + }, + { + "ancestorKind": "CommandList", + "region": "Statement", + "childIndex": 0, + "sourceStart": 16, + "sourceLength": 38 + } ], - "effectiveArguments": [ - { "clauseElementIndex": 2, "value": { "kind": "FiniteSet", "values": ["a", "b"], "pattern": null, "coveringDirectory": null } } + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s\\n" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "FiniteSet", + "values": [ + "a", + "b" + ], + "pattern": null, + "coveringDirectory": null + } + } ], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null }, + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { "kind": "Exact", "values": ["/work/out.txt"], "pattern": null, "coveringDirectory": null }, - "isPathRelevant": true, - "isComplete": true + "isComplete": true, + "fileMode": "Output", + "value": { + "kind": "Exact", + "values": [ + "/work/out.txt" + ], + "pattern": null, + "coveringDirectory": null + } } ] }, @@ -66,13 +230,57 @@ "immediateRole": "LoopBody", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 59 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 59 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 15, "sourceLength": 40 }, - { "ancestorKind": "CommandList", "region": "Statement", "childIndex": 1, "sourceStart": 16, "sourceLength": 38 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 59 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 59 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 15, + "sourceLength": 40 + }, + { + "ancestorKind": "CommandList", + "region": "Statement", + "childIndex": 1, + "sourceStart": 16, + "sourceLength": 38 + } + ], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "done" + ], + "pattern": null, + "coveringDirectory": null + } + } ], - "effectiveArguments": [], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/252_v03_for_binding_redirect.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/252_v03_for_binding_redirect.json index 96b3d79..1b1abf7 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/252_v03_for_binding_redirect.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/252_v03_for_binding_redirect.json @@ -1,27 +1,102 @@ { "name": "v0.3 Bash loop binding in redirect target", - "input": "for f in a b; do printf '%s' \"$f\" > \"$f.out\"; done", + "input": "for f in a b; do printf \u0027%s\u0027 \u0022$f\u0022 \u003E \u0022$f.out\u0022; done", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["printf"], + "verb": [ + "printf" + ], "args": [ - { "raw": "'%s'", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, - { "raw": "\"$f\"", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "\u0027%s\u0027", + "kind": "Literal", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + }, + { + "raw": "\u0022$f\u0022", + "kind": "EnvVar", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [ - { "direction": "Out", "target": "\"$f.out\"", "isDynamicSkip": true } + { + "direction": "Out", + "target": "\u0022$f.out\u0022", + "isDynamicSkip": true + } ] } ], "syntax": [ - { "kind": "Block", "parentIndex": null, "region": "Unknown", "childIndex": null, "sourceStart": 0, "sourceLength": 50, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "ForEach", "parentIndex": 0, "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 50, "clauseIndex": null, "groupKind": null, "listOperator": null, "bindingName": "f", "bindingRaw": "f", "bindingSourceStart": 4, "bindingSourceLength": 1, "iterableRaw": "a b", "iterableSourceStart": 9, "iterableSourceLength": 3 }, - { "kind": "Block", "parentIndex": 1, "region": "Iterator", "childIndex": null, "sourceStart": 9, "sourceLength": 3, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "Block", "parentIndex": 1, "region": "LoopBody", "childIndex": null, "sourceStart": 16, "sourceLength": 30, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "SimpleCommand", "parentIndex": 3, "region": "Statement", "childIndex": 0, "sourceStart": 17, "sourceLength": 27, "clauseIndex": 0, "groupKind": null, "listOperator": null } + { + "kind": "Block", + "parentIndex": null, + "region": "Unknown", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 50, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "ForEach", + "parentIndex": 0, + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 50, + "clauseIndex": null, + "groupKind": null, + "listOperator": null, + "bindingName": "f", + "bindingRaw": "f", + "bindingSourceStart": 4, + "bindingSourceLength": 1, + "iterableRaw": "a b", + "iterableSourceStart": 9, + "iterableSourceLength": 3 + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "Iterator", + "childIndex": null, + "sourceStart": 9, + "sourceLength": 3, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "LoopBody", + "childIndex": null, + "sourceStart": 16, + "sourceLength": 30, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 3, + "region": "Statement", + "childIndex": 0, + "sourceStart": 17, + "sourceLength": 27, + "clauseIndex": 0, + "groupKind": null, + "listOperator": null + } ], "commands": [ { @@ -29,14 +104,79 @@ "immediateRole": "LoopBody", "isComplete": false, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 50 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 50 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 16, "sourceLength": 30 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 50 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 50 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 16, + "sourceLength": 30 + } ], - "effectiveArguments": [ - { "clauseElementIndex": 2, "value": { "kind": "FiniteSet", "values": ["a", "b"], "pattern": null, "coveringDirectory": null } } + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "FiniteSet", + "values": [ + "a", + "b" + ], + "pattern": null, + "coveringDirectory": null + } + } ], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, + "redirects": [ + { + "redirectIndex": 0, + "kind": "FileRedirectAnalysis", + "sourceKind": "Default", + "sourceDescriptor": null, + "isComplete": false, + "fileMode": "Output", + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ] } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/255_v03_for_body_substitution.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/255_v03_for_body_substitution.json index c0a8108..368dc32 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/255_v03_for_body_substitution.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/255_v03_for_body_substitution.json @@ -1,36 +1,145 @@ { "name": "v0.3 Bash for-in body substitution inherits binding", - "input": "for f in a b; do printf '%s' \"$(echo \"$f\")\"; done", + "input": "for f in a b; do printf \u0027%s\u0027 \u0022$(echo \u0022$f\u0022)\u0022; done", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["echo"], + "verb": [ + "echo" + ], "args": [ - { "raw": "\"$f\"", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "\u0022$f\u0022", + "kind": "EnvVar", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [] }, { "operator": "None", - "verb": ["printf"], + "verb": [ + "printf" + ], "args": [ - { "raw": "'%s'", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, - { "raw": "\"$(echo \"$f\")\"", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "\u0027%s\u0027", + "kind": "Literal", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + }, + { + "raw": "\u0022$(echo \u0022$f\u0022)\u0022", + "kind": "DynamicSkip", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [] } ], "syntax": [ - { "kind": "Block", "parentIndex": null, "region": "Unknown", "childIndex": null, "sourceStart": 0, "sourceLength": 49, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "ForEach", "parentIndex": 0, "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 49, "clauseIndex": null, "groupKind": null, "listOperator": null, "bindingName": "f", "bindingRaw": "f", "bindingSourceStart": 4, "bindingSourceLength": 1, "iterableRaw": "a b", "iterableSourceStart": 9, "iterableSourceLength": 3 }, - { "kind": "Block", "parentIndex": 1, "region": "Iterator", "childIndex": null, "sourceStart": 9, "sourceLength": 3, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "Block", "parentIndex": 1, "region": "LoopBody", "childIndex": null, "sourceStart": 16, "sourceLength": 29, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "SimpleCommand", "parentIndex": 3, "region": "Statement", "childIndex": 0, "sourceStart": 17, "sourceLength": 26, "clauseIndex": 1, "groupKind": null, "listOperator": null }, - { "kind": "CommandSubstitution", "parentIndex": 4, "region": "Substitution", "childIndex": 0, "sourceStart": 30, "sourceLength": 12, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "Block", "parentIndex": 5, "region": "Substitution", "childIndex": 0, "sourceStart": 32, "sourceLength": 9, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "SimpleCommand", "parentIndex": 6, "region": "Statement", "childIndex": 0, "sourceStart": 32, "sourceLength": 9, "clauseIndex": 0, "groupKind": null, "listOperator": null } + { + "kind": "Block", + "parentIndex": null, + "region": "Unknown", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 49, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "ForEach", + "parentIndex": 0, + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 49, + "clauseIndex": null, + "groupKind": null, + "listOperator": null, + "bindingName": "f", + "bindingRaw": "f", + "bindingSourceStart": 4, + "bindingSourceLength": 1, + "iterableRaw": "a b", + "iterableSourceStart": 9, + "iterableSourceLength": 3 + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "Iterator", + "childIndex": null, + "sourceStart": 9, + "sourceLength": 3, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "LoopBody", + "childIndex": null, + "sourceStart": 16, + "sourceLength": 29, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 3, + "region": "Statement", + "childIndex": 0, + "sourceStart": 17, + "sourceLength": 26, + "clauseIndex": 1, + "groupKind": null, + "listOperator": null + }, + { + "kind": "CommandSubstitution", + "parentIndex": 4, + "region": "Substitution", + "childIndex": 0, + "sourceStart": 30, + "sourceLength": 12, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "Block", + "parentIndex": 5, + "region": "Substitution", + "childIndex": 0, + "sourceStart": 32, + "sourceLength": 9, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 6, + "region": "Statement", + "childIndex": 0, + "sourceStart": 32, + "sourceLength": 9, + "clauseIndex": 0, + "groupKind": null, + "listOperator": null + } ], "commands": [ { @@ -38,28 +147,108 @@ "immediateRole": "Substitution", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 49 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 49 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 16, "sourceLength": 29 }, - { "ancestorKind": "CommandSubstitution", "region": "Substitution", "childIndex": 0, "sourceStart": 30, "sourceLength": 12 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 32, "sourceLength": 9 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 49 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 49 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 16, + "sourceLength": 29 + }, + { + "ancestorKind": "CommandSubstitution", + "region": "Substitution", + "childIndex": 0, + "sourceStart": 30, + "sourceLength": 12 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 32, + "sourceLength": 9 + } ], - "effectiveArguments": [ - { "clauseElementIndex": 1, "value": { "kind": "FiniteSet", "values": ["a", "b"], "pattern": null, "coveringDirectory": null } } + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "FiniteSet", + "values": [ + "a", + "b" + ], + "pattern": null, + "coveringDirectory": null + } + } ], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } }, { "clauseIndex": 1, "immediateRole": "LoopBody", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 49 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 49 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 16, "sourceLength": 29 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 49 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 49 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 16, + "sourceLength": 29 + } ], - "effectiveArguments": [ + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s" + ], + "pattern": null, + "coveringDirectory": null + } + }, { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", @@ -69,7 +258,14 @@ } } ], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/257_v03_dynamic_descriptor_redirect.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/257_v03_dynamic_descriptor_redirect.json index e55e43e..393c878 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/257_v03_dynamic_descriptor_redirect.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/257_v03_dynamic_descriptor_redirect.json @@ -1,15 +1,21 @@ { "name": "v0.3 Bash computed descriptor target remains incomplete", - "input": "command 2>&$FD", + "input": "command 2\u003E\u0026$FD", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "ErrOut", "target": "&$FD", "isDynamicSkip": true } + { + "direction": "ErrOut", + "target": "\u0026$FD", + "isDynamicSkip": true + } ] } ], @@ -19,16 +25,29 @@ "immediateRole": "Ordinary", "isComplete": false, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 14 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 14 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, - "sourceKind": "Descriptor", - "sourceDescriptor": 2, - "operation": "DescriptorDuplicate", - "target": { "kind": "Unknown" }, - "isPathRelevant": false, + "kind": "UnresolvedRedirectAnalysis", + "sourceKind": "Unknown", + "sourceDescriptor": null, "isComplete": false } ] diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/258_v03_descriptor_move.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/258_v03_descriptor_move.json index abc0a22..63d56dd 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/258_v03_descriptor_move.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/258_v03_descriptor_move.json @@ -1,15 +1,21 @@ { "name": "v0.3 Bash static descriptor move", - "input": "command 2>&1-", + "input": "command 2\u003E\u00261-", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "ErrOut", "target": "&1-", "isDynamicSkip": true } + { + "direction": "ErrOut", + "target": "\u00261-", + "isDynamicSkip": true + } ] } ], @@ -19,18 +25,31 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 13 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 13 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorMoveRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "DescriptorMove", - "targetDescriptor": 1, - "target": { "kind": "Unknown" }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/259_v03_combined_output_redirect.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/259_v03_combined_output_redirect.json index 671cd38..2c487b1 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/259_v03_combined_output_redirect.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/259_v03_combined_output_redirect.json @@ -1,15 +1,20 @@ { "name": "v0.3 Bash combined output overwrite", - "input": "command &> out.log", + "input": "command \u0026\u003E out.log", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "Out", "target": "/work/out.log", "isDynamicSkip": false } + { + "direction": "Out", + "target": "/work/out.log" + } ] } ], @@ -19,16 +24,39 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 18 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 18 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", - "operation": "CombinedOutput", - "target": { "kind": "Exact", "values": ["/work/out.log"] }, - "isPathRelevant": true, - "isComplete": true + "sourceDescriptor": null, + "isComplete": true, + "fileMode": "CombinedOutput", + "value": { + "kind": "Exact", + "values": [ + "/work/out.log" + ], + "pattern": null, + "coveringDirectory": null + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/260_v03_combined_output_append.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/260_v03_combined_output_append.json index e2fc17c..0aca47b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/260_v03_combined_output_append.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/260_v03_combined_output_append.json @@ -1,15 +1,20 @@ { "name": "v0.3 Bash combined output append", - "input": "command &>> out.log", + "input": "command \u0026\u003E\u003E out.log", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "Append", "target": "/work/out.log", "isDynamicSkip": false } + { + "direction": "Append", + "target": "/work/out.log" + } ] } ], @@ -19,16 +24,39 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 19 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 19 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", - "operation": "CombinedOutputAppend", - "target": { "kind": "Exact", "values": ["/work/out.log"] }, - "isPathRelevant": true, - "isComplete": true + "sourceDescriptor": null, + "isComplete": true, + "fileMode": "CombinedOutputAppend", + "value": { + "kind": "Exact", + "values": [ + "/work/out.log" + ], + "pattern": null, + "coveringDirectory": null + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/261_v03_numeric_source_descriptor.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/261_v03_numeric_source_descriptor.json index a562d4a..9465abd 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/261_v03_numeric_source_descriptor.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/261_v03_numeric_source_descriptor.json @@ -1,15 +1,21 @@ { "name": "v0.3 Bash non-standard numeric source descriptor", - "input": "command 3>&1", + "input": "command 3\u003E\u00261", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "Out", "target": "&1", "isDynamicSkip": true } + { + "direction": "Out", + "target": "\u00261", + "isDynamicSkip": true + } ] } ], @@ -19,18 +25,31 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 12 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 12 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorDuplicateRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 3, - "operation": "DescriptorDuplicate", - "targetDescriptor": 1, - "target": { "kind": "Unknown" }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/262_v03_numeric_source_move.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/262_v03_numeric_source_move.json index 372de64..831572c 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/262_v03_numeric_source_move.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/262_v03_numeric_source_move.json @@ -1,15 +1,21 @@ { "name": "v0.3 Bash multi-digit source descriptor move", - "input": "command 10>&2-", + "input": "command 10\u003E\u00262-", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "Out", "target": "&2-", "isDynamicSkip": true } + { + "direction": "Out", + "target": "\u00262-", + "isDynamicSkip": true + } ] } ], @@ -19,18 +25,31 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 14 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 14 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorMoveRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 10, - "operation": "DescriptorMove", - "targetDescriptor": 2, - "target": { "kind": "Unknown" }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 2 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/263_v03_numeric_source_file_redirect.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/263_v03_numeric_source_file_redirect.json index c0f10ed..6913afc 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/263_v03_numeric_source_file_redirect.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/263_v03_numeric_source_file_redirect.json @@ -1,15 +1,20 @@ { "name": "v0.3 Bash numeric source file redirect", - "input": "command 3> out.log", + "input": "command 3\u003E out.log", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "Out", "target": "/work/out.log", "isDynamicSkip": false } + { + "direction": "Out", + "target": "/work/out.log" + } ] } ], @@ -19,17 +24,39 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 18 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 18 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 3, - "operation": "FileOutput", - "target": { "kind": "Exact", "values": ["/work/out.log"] }, - "isPathRelevant": true, - "isComplete": true + "isComplete": true, + "fileMode": "Output", + "value": { + "kind": "Exact", + "values": [ + "/work/out.log" + ], + "pattern": null, + "coveringDirectory": null + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/264_v03_overflow_source_descriptor.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/264_v03_overflow_source_descriptor.json index db455a1..0e2bcb3 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/264_v03_overflow_source_descriptor.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/264_v03_overflow_source_descriptor.json @@ -1,15 +1,20 @@ { "name": "v0.3 Bash overflow source descriptor fails closed", - "input": "command 999999999999999999999> out.log", + "input": "command 999999999999999999999\u003E out.log", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "Out", "target": "/work/out.log", "isDynamicSkip": false } + { + "direction": "Out", + "target": "/work/out.log" + } ] } ], @@ -19,15 +24,29 @@ "immediateRole": "Ordinary", "isComplete": false, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 38 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 38 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "UnresolvedRedirectAnalysis", "sourceKind": "Unknown", - "operation": "Unknown", - "target": { "kind": "Unknown" }, - "isPathRelevant": false, + "sourceDescriptor": null, "isComplete": false } ] diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/265_v03_continued_source_file_redirect.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/265_v03_continued_source_file_redirect.json index 67e4b96..f1b1706 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/265_v03_continued_source_file_redirect.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/265_v03_continued_source_file_redirect.json @@ -1,15 +1,20 @@ { "name": "v0.3 Bash continued numeric source file redirect", - "input": "command 3\\\n> out.log", + "input": "command 3\\\n\u003E out.log", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "Out", "target": "/work/out.log", "isDynamicSkip": false } + { + "direction": "Out", + "target": "/work/out.log" + } ] } ], @@ -19,17 +24,39 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 20 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 20 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 3, - "operation": "FileOutput", - "target": { "kind": "Exact", "values": ["/work/out.log"] }, - "isPathRelevant": true, - "isComplete": true + "isComplete": true, + "fileMode": "Output", + "value": { + "kind": "Exact", + "values": [ + "/work/out.log" + ], + "pattern": null, + "coveringDirectory": null + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/266_v03_continued_source_duplicate.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/266_v03_continued_source_duplicate.json index 1a0adaa..92fed96 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/266_v03_continued_source_duplicate.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/266_v03_continued_source_duplicate.json @@ -1,15 +1,21 @@ { "name": "v0.3 Bash continued numeric source duplicate", - "input": "command 3\\\n>&1", + "input": "command 3\\\n\u003E\u00261", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "Out", "target": "&1", "isDynamicSkip": true } + { + "direction": "Out", + "target": "\u00261", + "isDynamicSkip": true + } ] } ], @@ -19,18 +25,31 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 14 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 14 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorDuplicateRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 3, - "operation": "DescriptorDuplicate", - "targetDescriptor": 1, - "target": { "kind": "Unknown" }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/267_v03_continued_multidigit_source_move.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/267_v03_continued_multidigit_source_move.json index f0f845a..0b2018f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/267_v03_continued_multidigit_source_move.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/267_v03_continued_multidigit_source_move.json @@ -1,15 +1,21 @@ { "name": "v0.3 Bash continued multi-digit source move", - "input": "command 1\\\n0>&2-", + "input": "command 1\\\n0\u003E\u00262-", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["command"], + "verb": [ + "command" + ], "args": [], "redirects": [ - { "direction": "Out", "target": "&2-", "isDynamicSkip": true } + { + "direction": "Out", + "target": "\u00262-", + "isDynamicSkip": true + } ] } ], @@ -19,18 +25,31 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 16 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 16 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorMoveRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 10, - "operation": "DescriptorMove", - "targetDescriptor": 2, - "target": { "kind": "Unknown" }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 2 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/281_v03_design_literal_substitution_spellings.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/281_v03_design_literal_substitution_spellings.json index a22bc62..7ced2f2 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/281_v03_design_literal_substitution_spellings.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/281_v03_design_literal_substitution_spellings.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Quoted and escaped substitution-looking text does not execute", - "input": "printf '%s %s' '$(whoami)' \"\\$(id)\"", + "input": "printf \u0027%s %s\u0027 \u0027$(whoami)\u0027 \u0022\\$(id)\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -11,21 +11,21 @@ ], "args": [ { - "raw": "'%s %s'", + "raw": "\u0027%s %s\u0027", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, { - "raw": "'$(whoami)'", + "raw": "\u0027$(whoami)\u0027", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, { - "raw": "\"\\$(id)\"", + "raw": "\u0022\\$(id)\u0022", "kind": "Literal", "isPath": false, "resolved": "__NULL__", @@ -46,7 +46,7 @@ "isPath": false }, { - "raw": "'%s %s'", + "raw": "\u0027%s %s\u0027", "value": "%s %s", "role": "Argument", "sourceStart": 7, @@ -57,7 +57,7 @@ "isPath": false }, { - "raw": "'$(whoami)'", + "raw": "\u0027$(whoami)\u0027", "value": "$(whoami)", "role": "Argument", "sourceStart": 15, @@ -68,7 +68,7 @@ "isPath": false }, { - "raw": "\"\\$(id)\"", + "raw": "\u0022\\$(id)\u0022", "value": "$(id)", "role": "Argument", "sourceStart": 27, @@ -119,7 +119,44 @@ "sourceLength": 35 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s %s" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "$(whoami)" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "$(id)" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/282_v03_design_for_empty_iterable_preserves_cwd.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/282_v03_design_for_empty_iterable_preserves_cwd.json index a509525..1b0754d 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/282_v03_design_for_empty_iterable_preserves_cwd.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/282_v03_design_for_empty_iterable_preserves_cwd.json @@ -18,7 +18,7 @@ "isFlag": false }, { - "raw": "", + "raw": "\u003Cdynamic-cwd\u003E", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -196,7 +196,20 @@ "sourceLength": 10 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "/tmp" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -224,7 +237,7 @@ "sourceLength": 31 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/283_v03_design_decoded_wrapper_loop_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/283_v03_design_decoded_wrapper_loop_binding.json index 462ab6c..5f6a61a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/283_v03_design_decoded_wrapper_loop_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/283_v03_design_decoded_wrapper_loop_binding.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: A decoded child process does not receive an unexported loop binding", - "input": "for f in a; do bash -c 'printf \"%s\" \"$f\"'; done", + "input": "for f in a; do bash -c \u0027printf \u0022%s\u0022 \u0022$f\u0022\u0027; done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,14 +11,14 @@ ], "args": [ { - "raw": "\"%s\"", + "raw": "\u0022%s\u0022", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", @@ -39,7 +39,7 @@ "isPath": false }, { - "raw": "\"%s\"", + "raw": "\u0022%s\u0022", "value": "%s", "role": "Argument", "sourceStart": null, @@ -50,7 +50,7 @@ "isPath": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "value": "$f", "role": "Argument", "sourceStart": null, @@ -192,8 +192,21 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/284_v03_design_subshell_inherits_loop_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/284_v03_design_subshell_inherits_loop_binding.json index 8cdc6a8..cfd21ba 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/284_v03_design_subshell_inherits_loop_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/284_v03_design_subshell_inherits_loop_binding.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: A parenthesized subshell inherits shell bindings while isolating exit state", - "input": "for f in a; do (printf \"%s\" \"$f\"); done", + "input": "for f in a; do (printf \u0022%s\u0022 \u0022$f\u0022); done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,14 +11,14 @@ ], "args": [ { - "raw": "\"%s\"", + "raw": "\u0022%s\u0022", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", @@ -39,7 +39,7 @@ "isPath": false }, { - "raw": "\"%s\"", + "raw": "\u0022%s\u0022", "value": "%s", "role": "Argument", "sourceStart": 23, @@ -50,7 +50,7 @@ "isPath": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "value": "$f", "role": "Argument", "sourceStart": 28, @@ -192,8 +192,21 @@ "sourceLength": 16 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/285_v03_design_contextual_for_argument.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/285_v03_design_contextual_for_argument.json index 048d5b4..2ce04fb 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/285_v03_design_contextual_for_argument.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/285_v03_design_contextual_for_argument.json @@ -83,7 +83,20 @@ "sourceLength": 8 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "for" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/286_v03_design_here_string_literal_data.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/286_v03_design_here_string_literal_data.json index cbe40c1..5dc0fb3 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/286_v03_design_here_string_literal_data.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/286_v03_design_here_string_literal_data.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Static here-string data does not force a raw-command approval prompt", - "input": "cat <<< \"hello\"", + "input": "cat \u003C\u003C\u003C \u0022hello\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -30,7 +30,7 @@ "isPath": false }, { - "raw": "<<< \"hello\"", + "raw": "\u003C\u003C\u003C \u0022hello\u0022", "value": "hello", "role": "Redirect", "sourceStart": 4, @@ -81,7 +81,7 @@ "sourceLength": 15 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -93,20 +93,18 @@ "redirects": [ { "redirectIndex": 0, + "kind": "HereStringRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "HereString", - "targetDescriptor": null, - "target": { + "isComplete": true, + "value": { "kind": "Exact", "values": [ "hello\n" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": false, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/287_v03_design_here_string_dynamic_data.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/287_v03_design_here_string_dynamic_data.json index add2ef2..26a9b0e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/287_v03_design_here_string_dynamic_data.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/287_v03_design_here_string_dynamic_data.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Unknown stdin data does not make an otherwise complete redirect structurally incomplete", - "input": "cat <<< \"$value\"", + "input": "cat \u003C\u003C\u003C \u0022$value\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -13,7 +13,7 @@ "redirects": [ { "direction": "In", - "target": "\"$value\"", + "target": "\u0022$value\u0022", "isDynamicSkip": true } ], @@ -30,7 +30,7 @@ "isPath": false }, { - "raw": "<<< \"$value\"", + "raw": "\u003C\u003C\u003C \u0022$value\u0022", "value": "$value", "role": "Redirect", "sourceStart": 4, @@ -81,7 +81,7 @@ "sourceLength": 16 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -93,18 +93,16 @@ "redirects": [ { "redirectIndex": 0, + "kind": "HereStringRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "HereString", - "targetDescriptor": null, - "target": { + "isComplete": true, + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": false, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/288_v03_design_escaped_home_adjacent_fragment.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/288_v03_design_escaped_home_adjacent_fragment.json index f015b4e..f81efdc 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/288_v03_design_escaped_home_adjacent_fragment.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/288_v03_design_escaped_home_adjacent_fragment.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Escaped inline prefix composes with a quoted suffix", - "input": "curl --data=@\\$HOME\".json\" https://example.invalid/api", + "input": "curl --data=@\\$HOME\u0022.json\u0022 https://example.invalid/api", "expected": { "isUnparseable": false, "clauses": [ @@ -18,7 +18,7 @@ "isFlag": true }, { - "raw": "@\\$HOME\".json\"", + "raw": "@\\$HOME\u0022.json\u0022", "kind": "Literal", "isPath": true, "resolved": "/work/$HOME.json", @@ -46,7 +46,7 @@ "isPath": false }, { - "raw": "--data=@\\$HOME\".json\"", + "raw": "--data=@\\$HOME\u0022.json\u0022", "value": "--data=@$HOME.json", "role": "Argument", "sourceStart": 5, @@ -109,8 +109,21 @@ "sourceLength": 54 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--data" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 1, "value": { "kind": "Exact", @@ -120,6 +133,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid/api" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/289_v03_design_double_quoted_escaped_home.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/289_v03_design_double_quoted_escaped_home.json index 6acbfe6..8b06594 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/289_v03_design_double_quoted_escaped_home.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/289_v03_design_double_quoted_escaped_home.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Escape provenance survives within one double-quoted token", - "input": "cat \"\\$HOME.txt\"", + "input": "cat \u0022\\$HOME.txt\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"\\$HOME.txt\"", + "raw": "\u0022\\$HOME.txt\u0022", "kind": "Literal", "isPath": true, "resolved": "/work/$HOME.txt", @@ -32,7 +32,7 @@ "isPath": false }, { - "raw": "\"\\$HOME.txt\"", + "raw": "\u0022\\$HOME.txt\u0022", "value": "$HOME.txt", "role": "Argument", "sourceStart": 4, @@ -84,8 +84,9 @@ "sourceLength": 16 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/290_v03_design_double_quoted_literal_expandable.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/290_v03_design_double_quoted_literal_expandable.json index dfae576..7199b26 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/290_v03_design_double_quoted_literal_expandable.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/290_v03_design_double_quoted_literal_expandable.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Literal and expandable regions compose within one token", - "input": "echo \"\\${HOME}-$HOME\"", + "input": "echo \u0022\\${HOME}-$HOME\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"\\${HOME}-$HOME\"", + "raw": "\u0022\\${HOME}-$HOME\u0022", "kind": "Tilde", "isPath": false, "resolved": "__NULL__", @@ -32,7 +32,7 @@ "isPath": false }, { - "raw": "\"\\${HOME}-$HOME\"", + "raw": "\u0022\\${HOME}-$HOME\u0022", "value": "${HOME}-$HOME", "role": "Argument", "sourceStart": 5, @@ -83,8 +83,9 @@ "sourceLength": 21 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/291_v03_design_runtime_positional_parameter.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/291_v03_design_runtime_positional_parameter.json index 8f3e4b7..f03bb77 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/291_v03_design_runtime_positional_parameter.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/291_v03_design_runtime_positional_parameter.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Runtime positional parameters cannot become literal paths", - "input": "cat \"$1\"", + "input": "cat \u0022$1\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"$1\"", + "raw": "\u0022$1\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -32,7 +32,7 @@ "isPath": false }, { - "raw": "\"$1\"", + "raw": "\u0022$1\u0022", "value": "$1", "role": "Argument", "sourceStart": 4, @@ -83,8 +83,9 @@ "sourceLength": 8 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/292_v03_design_runtime_argument_vector.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/292_v03_design_runtime_argument_vector.json index 97a4408..63f0808 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/292_v03_design_runtime_argument_vector.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/292_v03_design_runtime_argument_vector.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Quoted dollar-at can produce zero or multiple arguments", - "input": "cat \"$@\"", + "input": "cat \u0022$@\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"$@\"", + "raw": "\u0022$@\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -32,7 +32,7 @@ "isPath": false }, { - "raw": "\"$@\"", + "raw": "\u0022$@\u0022", "value": "$@", "role": "Argument", "sourceStart": 4, @@ -83,8 +83,9 @@ "sourceLength": 8 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/293_v03_pwsh_payload_stays_bash_argument.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/293_v03_pwsh_payload_stays_bash_argument.json index 24e8161..7f896c5 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/293_v03_pwsh_payload_stays_bash_argument.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/293_v03_pwsh_payload_stays_bash_argument.json @@ -120,7 +120,44 @@ "sourceLength": 73 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-NoProfile" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "-Command" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "Get-Content input.txt | Set-Content output.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/294_v03_design_simple_command_substitution.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/294_v03_design_simple_command_substitution.json index c2c7b3f..7aa6082 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/294_v03_design_simple_command_substitution.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/294_v03_design_simple_command_substitution.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Simple command substitution", - "input": "rm \"$(find /tmp)\"", + "input": "rm \u0022$(find /tmp)\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -52,7 +52,7 @@ ], "args": [ { - "raw": "\"$(find /tmp)\"", + "raw": "\u0022$(find /tmp)\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -73,7 +73,7 @@ "isPath": false }, { - "raw": "\"$(find /tmp)\"", + "raw": "\u0022$(find /tmp)\u0022", "value": "$(find /tmp)", "role": "Argument", "sourceStart": 3, @@ -171,7 +171,20 @@ "sourceLength": 9 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "/tmp" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -179,8 +192,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } }, { "clauseIndex": 1, @@ -195,8 +207,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", @@ -213,8 +226,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/295_v03_design_multiple_command_substitutions.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/295_v03_design_multiple_command_substitutions.json index 7f96227..d742f73 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/295_v03_design_multiple_command_substitutions.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/295_v03_design_multiple_command_substitutions.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Multiple command substitutions", - "input": "printf '%s %s' \"$(first)\" \"$(second)\"", + "input": "printf \u0027%s %s\u0027 \u0022$(first)\u0022 \u0022$(second)\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -53,21 +53,21 @@ ], "args": [ { - "raw": "'%s %s'", + "raw": "\u0027%s %s\u0027", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, { - "raw": "\"$(first)\"", + "raw": "\u0022$(first)\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", "isFlag": false }, { - "raw": "\"$(second)\"", + "raw": "\u0022$(second)\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -88,7 +88,7 @@ "isPath": false }, { - "raw": "'%s %s'", + "raw": "\u0027%s %s\u0027", "value": "%s %s", "role": "Argument", "sourceStart": 7, @@ -99,7 +99,7 @@ "isPath": false }, { - "raw": "\"$(first)\"", + "raw": "\u0022$(first)\u0022", "value": "$(first)", "role": "Argument", "sourceStart": 15, @@ -110,7 +110,7 @@ "isPath": false }, { - "raw": "\"$(second)\"", + "raw": "\u0022$(second)\u0022", "value": "$(second)", "role": "Argument", "sourceStart": 26, @@ -241,7 +241,7 @@ "sourceLength": 5 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -249,8 +249,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } }, { "clauseIndex": 1, @@ -279,7 +278,7 @@ "sourceLength": 6 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -287,8 +286,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } }, { "clauseIndex": 2, @@ -303,8 +301,21 @@ "sourceLength": 37 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s %s" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", @@ -314,6 +325,7 @@ } }, { + "clauseArgumentIndex": 2, "clauseElementIndex": 3, "value": { "kind": "Unknown", @@ -330,8 +342,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/298_v03_design_command_substitution_isolated_cwd.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/298_v03_design_command_substitution_isolated_cwd.json index 90361df..a45398d 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/298_v03_design_command_substitution_isolated_cwd.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/298_v03_design_command_substitution_isolated_cwd.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Substitution cwd isolation", - "input": "printf '%s' \"$(cd /tmp && pwd)\"; cat relative.txt", + "input": "printf \u0027%s\u0027 \u0022$(cd /tmp \u0026\u0026 pwd)\u0022; cat relative.txt", "expected": { "isUnparseable": false, "clauses": [ @@ -82,14 +82,14 @@ ], "args": [ { - "raw": "'%s'", + "raw": "\u0027%s\u0027", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, { - "raw": "\"$(cd /tmp && pwd)\"", + "raw": "\u0022$(cd /tmp \u0026\u0026 pwd)\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -110,7 +110,7 @@ "isPath": false }, { - "raw": "'%s'", + "raw": "\u0027%s\u0027", "value": "%s", "role": "Argument", "sourceStart": 7, @@ -121,8 +121,8 @@ "isPath": false }, { - "raw": "\"$(cd /tmp && pwd)\"", - "value": "$(cd /tmp && pwd)", + "raw": "\u0022$(cd /tmp \u0026\u0026 pwd)\u0022", + "value": "$(cd /tmp \u0026\u0026 pwd)", "role": "Argument", "sourceStart": 12, "sourceLength": 19, @@ -318,7 +318,20 @@ "sourceLength": 14 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "/tmp" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -326,8 +339,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } }, { "clauseIndex": 1, @@ -370,7 +382,7 @@ "sourceLength": 14 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -378,8 +390,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } }, { "clauseIndex": 2, @@ -401,8 +412,21 @@ "sourceLength": 49 } ], - "effectiveArguments": [ + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s" + ], + "pattern": null, + "coveringDirectory": null + } + }, { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", @@ -419,8 +443,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } }, { "clauseIndex": 3, @@ -442,7 +465,20 @@ "sourceLength": 49 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "relative.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -450,8 +486,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/299_v03_design_cd_sequence_failure_join.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/299_v03_design_cd_sequence_failure_join.json index 438e2ae..b665655 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/299_v03_design_cd_sequence_failure_join.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/299_v03_design_cd_sequence_failure_join.json @@ -52,7 +52,7 @@ ], "args": [ { - "raw": "", + "raw": "\u003Cdynamic-cwd\u003E", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -143,7 +143,20 @@ "sourceLength": 14 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "/maybe" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -151,8 +164,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } }, { "clauseIndex": 1, @@ -174,14 +186,13 @@ "sourceLength": 14 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/300_v03_design_for_duplicate_order_final_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/300_v03_design_for_duplicate_order_final_binding.json index 0007989..79ba060 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/300_v03_design_for_duplicate_order_final_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/300_v03_design_for_duplicate_order_final_binding.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Ordered loop final binding", - "input": "for f in a b a; do :; done; printf '%s' \"$f\"", + "input": "for f in a b a; do :; done; printf \u0027%s\u0027 \u0022$f\u0022", "expected": { "isUnparseable": false, "clauses": [ @@ -32,14 +32,14 @@ ], "args": [ { - "raw": "'%s'", + "raw": "\u0027%s\u0027", "kind": "Literal", "isPath": false, "resolved": "__NULL__", "isFlag": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", @@ -60,7 +60,7 @@ "isPath": false }, { - "raw": "'%s'", + "raw": "\u0027%s\u0027", "value": "%s", "role": "Argument", "sourceStart": 35, @@ -71,7 +71,7 @@ "isPath": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "value": "$f", "role": "Argument", "sourceStart": 40, @@ -205,7 +205,7 @@ "sourceLength": 4 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -213,8 +213,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } }, { "clauseIndex": 1, @@ -236,8 +235,21 @@ "sourceLength": 44 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", @@ -256,8 +268,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/301_v03_design_for_native_option_injection.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/301_v03_design_for_native_option_injection.json index ba7c86a..83523e4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/301_v03_design_for_native_option_injection.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/301_v03_design_for_native_option_injection.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Loop option injection", - "input": "for f in -rf /tmp/x; do rm \"$f\"; done", + "input": "for f in -rf /tmp/x; do rm \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -32,7 +32,7 @@ "isPath": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "value": "$f", "role": "Argument", "sourceStart": 27, @@ -137,8 +137,9 @@ "sourceLength": 10 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", @@ -158,8 +159,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/302_v03_design_nested_for_cross_product.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/302_v03_design_nested_for_cross_product.json index ac8eeca..3036136 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/302_v03_design_nested_for_cross_product.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/302_v03_design_nested_for_cross_product.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Nested loop cross product", - "input": "for d in a b; do for f in x y; do echo \"$d/$f\"; done; done", + "input": "for d in a b; do for f in x y; do echo \u0022$d/$f\u0022; done; done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,7 +11,7 @@ ], "args": [ { - "raw": "\"$d/$f\"", + "raw": "\u0022$d/$f\u0022", "kind": "DynamicSkip", "isPath": false, "resolved": "__NULL__", @@ -32,7 +32,7 @@ "isPath": false }, { - "raw": "\"$d/$f\"", + "raw": "\u0022$d/$f\u0022", "value": "$d/$f", "role": "Argument", "sourceStart": 39, @@ -191,8 +191,9 @@ "sourceLength": 15 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", @@ -214,8 +215,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/303_v03_design_for_body_pipeline.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/303_v03_design_for_body_pipeline.json index 119ad2e..da62842 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/303_v03_design_for_body_pipeline.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/303_v03_design_for_body_pipeline.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Loop body pipeline", - "input": "for f in a b; do printf '%s\\n' \"$f\" | sort; done", + "input": "for f in a b; do printf \u0027%s\\n\u0027 \u0022$f\u0022 | sort; done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,14 +11,14 @@ ], "args": [ { - "raw": "'%s\\n'", + "raw": "\u0027%s\\n\u0027", "kind": "Literal", "isPath": true, "resolved": "/work/%s/n", "isFlag": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", @@ -39,7 +39,7 @@ "isPath": false }, { - "raw": "'%s\\n'", + "raw": "\u0027%s\\n\u0027", "value": "%s\\n", "role": "Argument", "sourceStart": 24, @@ -51,7 +51,7 @@ "resolved": "/work/%s/n" }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "value": "$f", "role": "Argument", "sourceStart": 31, @@ -206,8 +206,21 @@ "sourceLength": 25 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s\\n" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "FiniteSet", @@ -227,8 +240,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } }, { "clauseIndex": 1, @@ -264,7 +276,7 @@ "sourceLength": 25 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -272,8 +284,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/305_v03_design_for_candidate_cap_32.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/305_v03_design_for_candidate_cap_32.json index f4429d2..d8787a7 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/305_v03_design_for_candidate_cap_32.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/305_v03_design_for_candidate_cap_32.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Candidate cap", - "input": "for f in v01 v02 v03 v04 v05 v06 v07 v08 v09 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32; do printf '%s\\n' \"$f\"; done", + "input": "for f in v01 v02 v03 v04 v05 v06 v07 v08 v09 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32; do printf \u0027%s\\n\u0027 \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,14 +11,14 @@ ], "args": [ { - "raw": "'%s\\n'", + "raw": "\u0027%s\\n\u0027", "kind": "Literal", "isPath": true, "resolved": "/work/%s/n", "isFlag": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", @@ -39,7 +39,7 @@ "isPath": false }, { - "raw": "'%s\\n'", + "raw": "\u0027%s\\n\u0027", "value": "%s\\n", "role": "Argument", "sourceStart": 148, @@ -51,7 +51,7 @@ "resolved": "/work/%s/n" }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "value": "$f", "role": "Argument", "sourceStart": 155, @@ -156,8 +156,21 @@ "sourceLength": 21 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s\\n" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "FiniteSet", @@ -207,8 +220,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/306_v03_design_for_candidate_overflow_33.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/306_v03_design_for_candidate_overflow_33.json index 69034b8..c639644 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/306_v03_design_for_candidate_overflow_33.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/306_v03_design_for_candidate_overflow_33.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Candidate overflow", - "input": "for f in v01 v02 v03 v04 v05 v06 v07 v08 v09 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32 v33; do printf '%s\\n' \"$f\"; done", + "input": "for f in v01 v02 v03 v04 v05 v06 v07 v08 v09 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32 v33; do printf \u0027%s\\n\u0027 \u0022$f\u0022; done", "expected": { "isUnparseable": false, "clauses": [ @@ -11,14 +11,14 @@ ], "args": [ { - "raw": "'%s\\n'", + "raw": "\u0027%s\\n\u0027", "kind": "Literal", "isPath": true, "resolved": "/work/%s/n", "isFlag": false }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", @@ -39,7 +39,7 @@ "isPath": false }, { - "raw": "'%s\\n'", + "raw": "\u0027%s\\n\u0027", "value": "%s\\n", "role": "Argument", "sourceStart": 152, @@ -51,7 +51,7 @@ "resolved": "/work/%s/n" }, { - "raw": "\"$f\"", + "raw": "\u0022$f\u0022", "value": "$f", "role": "Argument", "sourceStart": 159, @@ -156,8 +156,21 @@ "sourceLength": 21 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "%s\\n" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", @@ -174,8 +187,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "redirects": [] + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/307_v03_design_static_fd_duplication.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/307_v03_design_static_fd_duplication.json index 5d0e5b7..e800e72 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/307_v03_design_static_fd_duplication.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/307_v03_design_static_fd_duplication.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Static descriptor duplication", - "input": "dotnet test 2>&1", + "input": "dotnet test 2\u003E\u00261", "expected": { "isUnparseable": false, "clauses": [ @@ -14,7 +14,7 @@ "redirects": [ { "direction": "ErrOut", - "target": "&1", + "target": "\u00261", "isDynamicSkip": true } ], @@ -42,8 +42,8 @@ "isPath": false }, { - "raw": "2>&1", - "value": "&1", + "raw": "2\u003E\u00261", + "value": "\u00261", "role": "Redirect", "sourceStart": 12, "sourceLength": 4, @@ -93,7 +93,7 @@ "sourceLength": 16 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -105,18 +105,11 @@ "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorDuplicateRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "DescriptorDuplicate", - "targetDescriptor": 1, - "target": { - "kind": "Unknown", - "values": [], - "pattern": null, - "coveringDirectory": null - }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/308_v03_design_heredoc_body_is_data.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/308_v03_design_heredoc_body_is_data.json index 9b382ee..1ac814a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/308_v03_design_heredoc_body_is_data.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/308_v03_design_heredoc_body_is_data.json @@ -1,6 +1,6 @@ { "name": "v0.3 Bash design promotion: Literal heredoc body is data", - "input": "cat > output.txt <<'EOF'\nhello\nEOF\n", + "input": "cat \u003E output.txt \u003C\u003C\u0027EOF\u0027\nhello\nEOF\n", "expected": { "isUnparseable": false, "clauses": [ @@ -18,7 +18,7 @@ }, { "direction": "In", - "target": "<", + "target": "\u003C\u003CEOF\u003E", "isDynamicSkip": false } ], @@ -35,7 +35,7 @@ "isPath": false }, { - "raw": "> output.txt", + "raw": "\u003E output.txt", "value": "output.txt", "role": "Redirect", "sourceStart": 4, @@ -47,7 +47,7 @@ "resolved": "/work/output.txt" }, { - "raw": "<<'EOF'", + "raw": "\u003C\u003C\u0027EOF\u0027", "value": "EOF", "role": "Redirect", "sourceStart": 17, @@ -98,7 +98,7 @@ "sourceLength": 35 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -110,51 +110,42 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "/work/output.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } }, - { - "redirectIndex": 1, - "sourceKind": "Default", - "sourceDescriptor": null, - "operation": "HereDocument", - "targetDescriptor": null, - "target": { - "kind": "Unknown", - "values": [], - "pattern": null, - "coveringDirectory": null - }, - "hereDocument": { - "delimiter": { - "raw": "'EOF'", - "sourceStart": 19, - "sourceLength": 5 - }, - "body": { - "raw": "hello\n", - "sourceStart": 25, - "sourceLength": 6 - }, - "expansionMode": "Literal", - "stripLeadingTabs": false, - "isComplete": true - }, - "isPathRelevant": false, - "isComplete": true - } + { + "redirectIndex": 1, + "kind": "HereDocumentRedirectAnalysis", + "sourceKind": "Default", + "sourceDescriptor": null, + "isComplete": true, + "hereDocument": { + "delimiter": { + "raw": "\u0027EOF\u0027", + "sourceStart": 19, + "sourceLength": 5 + }, + "body": { + "raw": "hello\n", + "sourceStart": 25, + "sourceLength": 6 + }, + "expansionMode": "Literal", + "stripLeadingTabs": false, + "isComplete": true + } + } ] } ] diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/309_v03_zero_or_more_loop_joins_zero_iteration_cwd.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/309_v03_zero_or_more_loop_joins_zero_iteration_cwd.json new file mode 100644 index 0000000..f36c865 --- /dev/null +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/309_v03_zero_or_more_loop_joins_zero_iteration_cwd.json @@ -0,0 +1,254 @@ +{ + "name": "v0.3 Bash: A zero-or-more loop joins the zero-iteration cwd path", + "input": "for f in /tmp/*.txt; do cd /tmp; done; pwd", + "expected": { + "isUnparseable": false, + "clauses": [ + { + "operator": "None", + "verb": ["cd"], + "args": [ + { + "raw": "/tmp", + "kind": "Literal", + "isPath": true, + "resolved": "/tmp", + "isFlag": false + }, + { + "raw": "\u003Cdynamic-cwd\u003E", + "kind": "DynamicSkip", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false, + "isCwdAttribution": true + } + ], + "redirects": [], + "elements": [ + { + "raw": "cd", + "value": "cd", + "role": "Verb", + "sourceStart": 24, + "sourceLength": 2, + "precedingVerbElementCount": 0, + "kind": "Literal", + "isFlag": false, + "isPath": false + }, + { + "raw": "/tmp", + "value": "/tmp", + "role": "Argument", + "sourceStart": 27, + "sourceLength": 4, + "precedingVerbElementCount": 1, + "kind": "Literal", + "isFlag": false, + "isPath": true, + "resolved": "/tmp" + } + ] + }, + { + "operator": "Sequence", + "verb": ["pwd"], + "args": [ + { + "raw": "\u003Cdynamic-cwd\u003E", + "kind": "DynamicSkip", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false, + "isCwdAttribution": true + } + ], + "redirects": [], + "elements": [ + { + "raw": "pwd", + "value": "pwd", + "role": "Verb", + "sourceStart": 39, + "sourceLength": 3, + "precedingVerbElementCount": 0, + "kind": "Literal", + "isFlag": false, + "isPath": false + } + ] + } + ], + "syntax": [ + { + "kind": "Block", + "parentIndex": null, + "region": "Unknown", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 42, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "CommandList", + "parentIndex": 0, + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 42, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "ForEach", + "parentIndex": 1, + "region": "Statement", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 37, + "clauseIndex": null, + "groupKind": null, + "listOperator": "None", + "bindingName": "f", + "bindingRaw": "f", + "bindingSourceStart": 4, + "bindingSourceLength": 1, + "iterableRaw": "/tmp/*.txt", + "iterableSourceStart": 9, + "iterableSourceLength": 10 + }, + { + "kind": "Block", + "parentIndex": 2, + "region": "Iterator", + "childIndex": null, + "sourceStart": 9, + "sourceLength": 10, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "Block", + "parentIndex": 2, + "region": "LoopBody", + "childIndex": null, + "sourceStart": 23, + "sourceLength": 10, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 4, + "region": "Statement", + "childIndex": 0, + "sourceStart": 24, + "sourceLength": 7, + "clauseIndex": 0, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 1, + "region": "Statement", + "childIndex": 1, + "sourceStart": 39, + "sourceLength": 3, + "clauseIndex": 1, + "groupKind": null, + "listOperator": "Sequence" + } + ], + "commands": [ + { + "clauseIndex": 0, + "immediateRole": "LoopBody", + "isComplete": true, + "ancestry": [ + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 42 + }, + { + "ancestorKind": "CommandList", + "region": "Statement", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 42 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 37 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 23, + "sourceLength": 10 + } + ], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": ["/tmp"], + "pattern": null, + "coveringDirectory": null + } + } + ], + "workingDirectory": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseIndex": 1, + "immediateRole": "Ordinary", + "isComplete": true, + "ancestry": [ + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 42 + }, + { + "ancestorKind": "CommandList", + "region": "Statement", + "childIndex": 1, + "sourceStart": 0, + "sourceLength": 42 + } + ], + "arguments": [], + "workingDirectory": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ] + }, + "notes": "Pins the zero-iteration path for an unmatched static pattern: the loop body may change cwd, but the post-loop cwd must join with the incoming state and remain unknown." +} diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/41_redirect_out_and_err.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/41_redirect_out_and_err.json index 45131dc..b8d515f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/41_redirect_out_and_err.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/41_redirect_out_and_err.json @@ -1,19 +1,25 @@ { - "name": "Multiple redirects: cmd > out 2> err", - "input": "cmd > out 2> err", + "name": "Multiple redirects: cmd \u003E out 2\u003E err", + "input": "cmd \u003E out 2\u003E err", "expected": { "isUnparseable": false, "clauses": [ { "operator": "None", - "verb": ["cmd"], + "verb": [ + "cmd" + ], "args": [], "redirects": [ - { "direction": "Out", "target": "/work/out" }, - { "direction": "ErrOut", "target": "/work/err" } - ], - "isSubshell": false, - "isCommandStringWrapped": false + { + "direction": "Out", + "target": "/work/out" + }, + { + "direction": "ErrOut", + "target": "/work/err" + } + ] } ], "commands": [ @@ -22,25 +28,55 @@ "immediateRole": "Ordinary", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 16 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 16 + } ], + "arguments": [], + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + }, "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", - "operation": "FileOutput", - "target": { "kind": "Exact", "values": ["/work/out"] }, - "isPathRelevant": true, - "isComplete": true + "sourceDescriptor": null, + "isComplete": true, + "fileMode": "Output", + "value": { + "kind": "Exact", + "values": [ + "/work/out" + ], + "pattern": null, + "coveringDirectory": null + } }, { "redirectIndex": 1, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "FileOutput", - "target": { "kind": "Exact", "values": ["/work/err"] }, - "isPathRelevant": true, - "isComplete": true + "isComplete": true, + "fileMode": "Output", + "value": { + "kind": "Exact", + "values": [ + "/work/err" + ], + "pattern": null, + "coveringDirectory": null + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/bash/46_unparseable_for_loop.json b/tests/ShellSyntaxTree.Tests/Corpus/bash/46_unparseable_for_loop.json index abd0cd0..0eda966 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/bash/46_unparseable_for_loop.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/bash/46_unparseable_for_loop.json @@ -6,19 +6,84 @@ "clauses": [ { "operator": "None", - "verb": ["echo"], + "verb": [ + "echo" + ], "args": [ - { "raw": "$i", "kind": "EnvVar", "isPath": false, "resolved": "__NULL__", "isFlag": false } + { + "raw": "$i", + "kind": "EnvVar", + "isPath": false, + "resolved": "__NULL__", + "isFlag": false + } ], "redirects": [] } ], "syntax": [ - { "kind": "Block", "parentIndex": null, "region": "Unknown", "childIndex": null, "sourceStart": 0, "sourceLength": 30, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "ForEach", "parentIndex": 0, "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 30, "clauseIndex": null, "groupKind": null, "listOperator": null, "bindingName": "i", "bindingRaw": "i", "bindingSourceStart": 4, "bindingSourceLength": 1, "iterableRaw": "1 2", "iterableSourceStart": 9, "iterableSourceLength": 3 }, - { "kind": "Block", "parentIndex": 1, "region": "Iterator", "childIndex": null, "sourceStart": 9, "sourceLength": 3, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "Block", "parentIndex": 1, "region": "LoopBody", "childIndex": null, "sourceStart": 16, "sourceLength": 10, "clauseIndex": null, "groupKind": null, "listOperator": null }, - { "kind": "SimpleCommand", "parentIndex": 3, "region": "Statement", "childIndex": 0, "sourceStart": 17, "sourceLength": 7, "clauseIndex": 0, "groupKind": null, "listOperator": null } + { + "kind": "Block", + "parentIndex": null, + "region": "Unknown", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 30, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "ForEach", + "parentIndex": 0, + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 30, + "clauseIndex": null, + "groupKind": null, + "listOperator": null, + "bindingName": "i", + "bindingRaw": "i", + "bindingSourceStart": 4, + "bindingSourceLength": 1, + "iterableRaw": "1 2", + "iterableSourceStart": 9, + "iterableSourceLength": 3 + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "Iterator", + "childIndex": null, + "sourceStart": 9, + "sourceLength": 3, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "Block", + "parentIndex": 1, + "region": "LoopBody", + "childIndex": null, + "sourceStart": 16, + "sourceLength": 10, + "clauseIndex": null, + "groupKind": null, + "listOperator": null + }, + { + "kind": "SimpleCommand", + "parentIndex": 3, + "region": "Statement", + "childIndex": 0, + "sourceStart": 17, + "sourceLength": 7, + "clauseIndex": 0, + "groupKind": null, + "listOperator": null + } ], "commands": [ { @@ -26,14 +91,48 @@ "immediateRole": "LoopBody", "isComplete": true, "ancestry": [ - { "ancestorKind": "Block", "region": "Root", "childIndex": 0, "sourceStart": 0, "sourceLength": 30 }, - { "ancestorKind": "ForEach", "region": "LoopBody", "childIndex": null, "sourceStart": 0, "sourceLength": 30 }, - { "ancestorKind": "Block", "region": "Statement", "childIndex": 0, "sourceStart": 16, "sourceLength": 10 } + { + "ancestorKind": "Block", + "region": "Root", + "childIndex": 0, + "sourceStart": 0, + "sourceLength": 30 + }, + { + "ancestorKind": "ForEach", + "region": "LoopBody", + "childIndex": null, + "sourceStart": 0, + "sourceLength": 30 + }, + { + "ancestorKind": "Block", + "region": "Statement", + "childIndex": 0, + "sourceStart": 16, + "sourceLength": 10 + } ], - "effectiveArguments": [ - { "clauseElementIndex": 1, "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null } } + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } ], - "workingDirectory": { "kind": "Exact", "values": ["/work"], "pattern": null, "coveringDirectory": null } + "workingDirectory": { + "kind": "Exact", + "values": [ + "/work" + ], + "pattern": null, + "coveringDirectory": null + } } ] }, diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/128_redirect_out.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/128_redirect_out.json index 3abcfae..2842fbb 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/128_redirect_out.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/128_redirect_out.json @@ -56,7 +56,7 @@ "sourceLength": 18 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/out.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/129_redirect_append.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/129_redirect_append.json index 3b25845..7e9adad 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/129_redirect_append.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/129_redirect_append.json @@ -56,7 +56,7 @@ "sourceLength": 19 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileAppend", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Append", + "value": { "kind": "Exact", "values": [ "C:/work/log.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/130_redirect_err.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/130_redirect_err.json index a834713..e428f68 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/130_redirect_err.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/130_redirect_err.json @@ -56,7 +56,7 @@ "sourceLength": 22 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/err.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 d33e97f..7bd4939 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/131_redirect_err_append.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/131_redirect_err_append.json @@ -56,7 +56,7 @@ "sourceLength": 23 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "FileAppend", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Append", + "value": { "kind": "Exact", "values": [ "C:/work/err.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/132_redirect_verbose.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/132_redirect_verbose.json index 2c5b695..e7d0430 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/132_redirect_verbose.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/132_redirect_verbose.json @@ -56,7 +56,7 @@ "sourceLength": 23 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 3, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/verbose.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/133_redirect_warning.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/133_redirect_warning.json index fed069e..00a37a5 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/133_redirect_warning.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/133_redirect_warning.json @@ -56,7 +56,7 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 4, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/warn.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/134_redirect_debug.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/134_redirect_debug.json index 1e85faa..5af11b9 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/134_redirect_debug.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/134_redirect_debug.json @@ -56,7 +56,7 @@ "sourceLength": 21 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 5, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/debug.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/135_redirect_information.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/135_redirect_information.json index fdb1c93..8c57c90 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/135_redirect_information.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/135_redirect_information.json @@ -56,7 +56,7 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 6, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/info.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/136_redirect_all.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/136_redirect_all.json index 4b0a8c5..260028c 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/136_redirect_all.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/136_redirect_all.json @@ -56,7 +56,7 @@ "sourceLength": 19 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "PowerShellAllStreams", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/all.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 9583d47..0619870 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/137_redirect_all_append.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/137_redirect_all_append.json @@ -56,7 +56,7 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -68,20 +68,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "PowerShellAllStreams", "sourceDescriptor": null, - "operation": "FileAppend", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Append", + "value": { "kind": "Exact", "values": [ "C:/work/all.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 9d385c4..87027da 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/138_redirect_merge_2to1.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/138_redirect_merge_2to1.json @@ -57,7 +57,7 @@ "sourceLength": 16 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -69,18 +69,11 @@ "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorDuplicateRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "DescriptorDuplicate", - "targetDescriptor": 1, - "target": { - "kind": "Unknown", - "values": [], - "pattern": null, - "coveringDirectory": null - }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } 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 c8110a8..5414992 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/139_redirect_merge_3to1.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/139_redirect_merge_3to1.json @@ -57,7 +57,7 @@ "sourceLength": 13 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -69,18 +69,11 @@ "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorDuplicateRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 3, - "operation": "DescriptorDuplicate", - "targetDescriptor": 1, - "target": { - "kind": "Unknown", - "values": [], - "pattern": null, - "coveringDirectory": null - }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/140_redirect_null.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/140_redirect_null.json index c51641a..86dc4bd 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/140_redirect_null.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/140_redirect_null.json @@ -57,7 +57,7 @@ "sourceLength": 16 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -69,17 +69,9 @@ "redirects": [ { "redirectIndex": 0, - "sourceKind": "Default", + "kind": "UnresolvedRedirectAnalysis", + "sourceKind": "Unknown", "sourceDescriptor": null, - "operation": "Unknown", - "targetDescriptor": null, - "target": { - "kind": "Unknown", - "values": [], - "pattern": null, - "coveringDirectory": null - }, - "isPathRelevant": false, "isComplete": false } ] 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 54d5797..2031abd 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/162_dynamic_call_scriptblock.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/162_dynamic_call_scriptblock.json @@ -92,7 +92,7 @@ "sourceLength": 10 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ 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 777e16a..a2afcda 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 @@ -116,8 +116,21 @@ "sourceLength": 29 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-LiteralPath" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", 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 4b0368a..f326035 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 @@ -142,7 +142,20 @@ "sourceLength": 22 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "C:\\input" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -179,8 +192,21 @@ "sourceLength": 29 } ], - "effectiveArguments": [ + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-LiteralPath" + ], + "pattern": null, + "coveringDirectory": null + } + }, { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", 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 3e16b48..37ded72 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 @@ -173,7 +173,20 @@ "sourceLength": 22 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "C:\\input" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -210,8 +223,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/345_v03_foreach_body_pipeline.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/345_v03_foreach_body_pipeline.json index 442cac6..3386e29 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/345_v03_foreach_body_pipeline.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/345_v03_foreach_body_pipeline.json @@ -149,8 +149,9 @@ "sourceLength": 29 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", @@ -206,7 +207,7 @@ "sourceLength": 29 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/346_v03_foreach_parenthesized_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/346_v03_foreach_parenthesized_alias.json index 4d5df27..778ffb4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/346_v03_foreach_parenthesized_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/346_v03_foreach_parenthesized_alias.json @@ -102,7 +102,20 @@ "sourceLength": 29 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "x" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -132,7 +145,18 @@ "sourceLength": 29 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ 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 d12c260..367c6ea 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 @@ -58,7 +58,18 @@ "sourceLength": 14 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/348_v03_foreach_numeric_pipeline_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/348_v03_foreach_numeric_pipeline_alias.json index 360b01c..66d9c73 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/348_v03_foreach_numeric_pipeline_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/348_v03_foreach_numeric_pipeline_alias.json @@ -102,7 +102,20 @@ "sourceLength": 28 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "x" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -132,7 +145,18 @@ "sourceLength": 28 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ 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 8d24461..b3b601d 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 @@ -58,7 +58,18 @@ "sourceLength": 13 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ 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 aa78928..640161f 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 @@ -153,7 +153,7 @@ "sourceLength": 60 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -197,8 +197,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", @@ -237,7 +238,7 @@ "sourceLength": 60 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ 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 31cdab7..bfdc85c 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 @@ -185,8 +185,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", @@ -225,7 +226,7 @@ "sourceLength": 63 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ 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 cca6f24..d5130a4 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 @@ -167,8 +167,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", @@ -207,7 +208,7 @@ "sourceLength": 62 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -237,7 +238,7 @@ "sourceLength": 62 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/362_v03_foreach_isolated_exact_scalar.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/362_v03_foreach_isolated_exact_scalar.json index 6b418fa..8a4d502 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/362_v03_foreach_isolated_exact_scalar.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/362_v03_foreach_isolated_exact_scalar.json @@ -117,8 +117,21 @@ "sourceLength": 29 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-LiteralPath" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/363_v03_foreach_isolated_finite_array.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/363_v03_foreach_isolated_finite_array.json index f04993e..0fef572 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/363_v03_foreach_isolated_finite_array.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/363_v03_foreach_isolated_finite_array.json @@ -112,8 +112,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/364_v03_foreach_isolated_nested_composition.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/364_v03_foreach_isolated_nested_composition.json index 94db2e3..9b26deb 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/364_v03_foreach_isolated_nested_composition.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/364_v03_foreach_isolated_nested_composition.json @@ -166,8 +166,9 @@ "sourceLength": 30 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/365_v03_foreach_pipeline_objects_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/365_v03_foreach_pipeline_objects_unknown.json index a14fe3b..3ff1740 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/365_v03_foreach_pipeline_objects_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/365_v03_foreach_pipeline_objects_unknown.json @@ -138,7 +138,20 @@ "sourceLength": 22 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "C:\\input" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -175,8 +188,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/366_v03_foreach_candidate_overflow_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/366_v03_foreach_candidate_overflow_unknown.json index 0968417..58df5c1 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/366_v03_foreach_candidate_overflow_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/366_v03_foreach_candidate_overflow_unknown.json @@ -112,8 +112,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/368_v03_foreach_null_object_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/368_v03_foreach_null_object_unknown.json index 9bca914..7d82665 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/368_v03_foreach_null_object_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/368_v03_foreach_null_object_unknown.json @@ -112,8 +112,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/369_v03_foreach_ordered_persistent_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/369_v03_foreach_ordered_persistent_binding.json index 31abb4b..ed6f600 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/369_v03_foreach_ordered_persistent_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/369_v03_foreach_ordered_persistent_binding.json @@ -155,8 +155,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", @@ -198,8 +199,9 @@ "sourceLength": 67 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", 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 09cec0b..1f5508b 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 @@ -150,7 +150,20 @@ "sourceLength": 21 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "C:\\tmp" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -178,7 +191,7 @@ "sourceLength": 57 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/371_v03_foreach_location_failure_join.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/371_v03_foreach_location_failure_join.json index c35b9af..e368481 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/371_v03_foreach_location_failure_join.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/371_v03_foreach_location_failure_join.json @@ -161,8 +161,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", @@ -202,7 +203,18 @@ "sourceLength": 77 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/373_v03_foreach_object_parallel_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/373_v03_foreach_object_parallel_region.json index 7293bd9..c77ef87 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/373_v03_foreach_object_parallel_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/373_v03_foreach_object_parallel_region.json @@ -224,7 +224,7 @@ "sourceLength": 59 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -254,7 +254,30 @@ "sourceLength": 59 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -298,8 +321,9 @@ "sourceLength": 16 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/374_v03_foreach_object_parallel_process_state_escape.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/374_v03_foreach_object_parallel_process_state_escape.json index fc50a18..a67f91e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/374_v03_foreach_object_parallel_process_state_escape.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/374_v03_foreach_object_parallel_process_state_escape.json @@ -313,7 +313,7 @@ "sourceLength": 69 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -350,7 +350,30 @@ "sourceLength": 69 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -401,7 +424,32 @@ "sourceLength": 38 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:SST_PARALLEL_PATH" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -429,7 +477,20 @@ "sourceLength": 88 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/375_v03_foreach_object_parallel_fresh_process_state_escape.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/375_v03_foreach_object_parallel_fresh_process_state_escape.json index 83ca9ea..6d9f2de 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/375_v03_foreach_object_parallel_fresh_process_state_escape.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/375_v03_foreach_object_parallel_fresh_process_state_escape.json @@ -343,7 +343,7 @@ "sourceLength": 103 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -373,7 +373,66 @@ "sourceLength": 103 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-ThrottleLimit" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Exact", + "values": [ + "1" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 4, + "clauseElementIndex": 5, + "value": { + "kind": "Exact", + "values": [ + "-UseNewRunspace" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -424,7 +483,7 @@ "sourceLength": 35 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Unknown", "values": [], @@ -473,7 +532,32 @@ "sourceLength": 35 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:PATH" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/376_v03_foreach_object_parallel_rebound_process_escape.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/376_v03_foreach_object_parallel_rebound_process_escape.json index f4ef36a..f30addf 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/376_v03_foreach_object_parallel_rebound_process_escape.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/376_v03_foreach_object_parallel_rebound_process_escape.json @@ -389,7 +389,7 @@ "sourceLength": 111 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -426,7 +426,30 @@ "sourceLength": 111 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -484,7 +507,32 @@ "sourceLength": 78 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "sstSet" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Set-Item" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -540,7 +588,32 @@ "sourceLength": 78 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:SST_PARALLEL_ALIAS_ESCAPE_91F62F1F" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -568,7 +641,20 @@ "sourceLength": 130 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/377_v03_foreach_object_parallel_runspace_global_isolated.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/377_v03_foreach_object_parallel_runspace_global_isolated.json index cc74b14..17fba7a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/377_v03_foreach_object_parallel_runspace_global_isolated.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/377_v03_foreach_object_parallel_runspace_global_isolated.json @@ -341,7 +341,7 @@ "sourceLength": 74 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -378,7 +378,30 @@ "sourceLength": 74 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -429,7 +452,56 @@ "sourceLength": 43 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "sstChild" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Scope" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Exact", + "values": [ + "Global" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -459,7 +531,20 @@ "sourceLength": 93 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/378_v03_foreach_object_parallel_rebound_mutator_escape.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/378_v03_foreach_object_parallel_rebound_mutator_escape.json index 8f6d15a..f3a6536 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/378_v03_foreach_object_parallel_rebound_mutator_escape.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/378_v03_foreach_object_parallel_rebound_mutator_escape.json @@ -437,7 +437,7 @@ "sourceLength": 149 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -474,7 +474,42 @@ "sourceLength": 149 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-UseNewRunspace" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -532,7 +567,56 @@ "sourceLength": 100 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Name" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Set-Alias" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Value" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Exact", + "values": [ + "Set-Item" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -588,7 +672,32 @@ "sourceLength": 100 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:SST_PARALLEL_REBOUND_MUTATOR_91F62F1F" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -616,7 +725,20 @@ "sourceLength": 168 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/379_v03_foreach_object_parallel_unrelated_rebinding_isolated.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/379_v03_foreach_object_parallel_unrelated_rebinding_isolated.json index 2cb0d70..a00d466 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/379_v03_foreach_object_parallel_unrelated_rebinding_isolated.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/379_v03_foreach_object_parallel_unrelated_rebinding_isolated.json @@ -389,7 +389,7 @@ "sourceLength": 91 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -426,7 +426,30 @@ "sourceLength": 91 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -484,7 +507,32 @@ "sourceLength": 58 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "other" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Set-Item" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -542,7 +590,32 @@ "sourceLength": 58 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "untouched" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Write-Output" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -570,7 +643,20 @@ "sourceLength": 110 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/380_v03_foreach_object_parallel_function_provider_escape.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/380_v03_foreach_object_parallel_function_provider_escape.json index d8d9f9c..216b253 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/380_v03_foreach_object_parallel_function_provider_escape.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/380_v03_foreach_object_parallel_function_provider_escape.json @@ -465,7 +465,7 @@ "sourceLength": 155 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -502,7 +502,30 @@ "sourceLength": 155 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -560,7 +583,30 @@ "sourceLength": 122 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Function:global:sstProviderSet" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -630,8 +676,9 @@ "sourceLength": 16 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", @@ -696,7 +743,32 @@ "sourceLength": 122 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:SST_PARALLEL_FUNCTION_ESCAPE_91F62F1F" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -724,7 +796,20 @@ "sourceLength": 174 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/381_v03_foreach_object_parallel_double_colon_alias_escape.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/381_v03_foreach_object_parallel_double_colon_alias_escape.json index 4f6e78d..e9334a4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/381_v03_foreach_object_parallel_double_colon_alias_escape.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/381_v03_foreach_object_parallel_double_colon_alias_escape.json @@ -389,7 +389,7 @@ "sourceLength": 133 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -426,7 +426,30 @@ "sourceLength": 133 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -484,8 +507,9 @@ "sourceLength": 100 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", @@ -495,6 +519,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Set-Item" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { @@ -552,7 +588,32 @@ "sourceLength": 100 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:SST_PARALLEL_DOUBLE_ALIAS_91F62F1F" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -580,7 +641,20 @@ "sourceLength": 152 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/382_v03_foreach_object_parallel_double_colon_function_escape.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/382_v03_foreach_object_parallel_double_colon_function_escape.json index 488e264..c197efe 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/382_v03_foreach_object_parallel_double_colon_function_escape.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/382_v03_foreach_object_parallel_double_colon_function_escape.json @@ -465,7 +465,7 @@ "sourceLength": 155 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -502,7 +502,30 @@ "sourceLength": 155 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -560,8 +583,9 @@ "sourceLength": 122 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", @@ -571,6 +595,16 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { @@ -642,8 +676,9 @@ "sourceLength": 16 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", @@ -708,7 +743,32 @@ "sourceLength": 122 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:SST_PARALLEL_DOUBLE_FUNCTION_91F62F1F" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -736,7 +796,20 @@ "sourceLength": 174 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/383_v03_foreach_object_parallel_provider_name_colon_precision.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/383_v03_foreach_object_parallel_provider_name_colon_precision.json index dfa3ec4..b2c6953 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/383_v03_foreach_object_parallel_provider_name_colon_precision.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/383_v03_foreach_object_parallel_provider_name_colon_precision.json @@ -389,7 +389,7 @@ "sourceLength": 136 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -426,7 +426,30 @@ "sourceLength": 136 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -484,8 +507,9 @@ "sourceLength": 103 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", @@ -495,6 +519,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Set-Item" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { @@ -554,7 +590,32 @@ "sourceLength": 103 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:SST_PARALLEL_PROVIDER_COLON_91F62F1F" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -582,7 +643,20 @@ "sourceLength": 155 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], 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 a1c50ce..275a1e7 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 @@ -281,7 +281,54 @@ "sourceLength": 90 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -325,7 +372,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -353,7 +413,20 @@ "sourceLength": 90 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ 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 4278118..6ec799c 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 @@ -297,7 +297,66 @@ "sourceLength": 97 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-AsJob" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 4, + "clauseElementIndex": 5, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -341,7 +400,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -369,7 +441,20 @@ "sourceLength": 97 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ 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 1e1ae4b..f78678f 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 @@ -297,7 +297,66 @@ "sourceLength": 113 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-InDisconnectedSession" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 4, + "clauseElementIndex": 5, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -341,7 +400,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -369,7 +441,20 @@ "sourceLength": 113 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/387_v03_invoke_command_remote_multiple_targets.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/387_v03_invoke_command_remote_multiple_targets.json index 56ad03c..1e56fb9 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/387_v03_invoke_command_remote_multiple_targets.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/387_v03_invoke_command_remote_multiple_targets.json @@ -297,7 +297,64 @@ "sourceLength": 100 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "server2" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 4, + "clauseElementIndex": 5, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -341,7 +398,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -369,7 +439,20 @@ "sourceLength": 100 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/388_v03_invoke_command_remote_inline_target_array.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/388_v03_invoke_command_remote_inline_target_array.json index b9e2763..24d740f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/388_v03_invoke_command_remote_inline_target_array.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/388_v03_invoke_command_remote_inline_target_array.json @@ -286,8 +286,21 @@ "sourceLength": 100 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 1, "value": { "kind": "Unknown", @@ -295,6 +308,40 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server2" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 4, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { @@ -340,7 +387,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -368,7 +428,20 @@ "sourceLength": 100 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/389_v03_invoke_command_remote_dynamic_session.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/389_v03_invoke_command_remote_dynamic_session.json index 6fcc2f5..55a669e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/389_v03_invoke_command_remote_dynamic_session.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/389_v03_invoke_command_remote_dynamic_session.json @@ -281,7 +281,52 @@ "sourceLength": 87 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Session" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -325,7 +370,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -353,7 +411,20 @@ "sourceLength": 87 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ 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 92e7f82..4cbebb0 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 @@ -560,7 +560,52 @@ "sourceLength": 220 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ConnectionUri" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -611,7 +656,20 @@ "sourceLength": 94 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "/tmp" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -660,7 +718,32 @@ "sourceLength": 94 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Measure-Command" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Write-Output" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -709,7 +792,32 @@ "sourceLength": 94 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:SST_REMOTE_CHILD" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "child" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -737,7 +845,20 @@ "sourceLength": 220 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -767,7 +888,18 @@ "sourceLength": 220 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -811,7 +943,7 @@ "sourceLength": 10 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ 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 46e829a..de3b295 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 @@ -281,7 +281,52 @@ "sourceLength": 110 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-SSHConnection" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -325,7 +370,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -353,7 +411,20 @@ "sourceLength": 110 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ 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 b9461d9..5266867 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 @@ -281,7 +281,54 @@ "sourceLength": 100 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server1,server2" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -325,7 +372,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -353,7 +413,20 @@ "sourceLength": 100 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/393_v03_invoke_command_remote_positional_target_array.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/393_v03_invoke_command_remote_positional_target_array.json index a194141..dd6d4f5 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/393_v03_invoke_command_remote_positional_target_array.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/393_v03_invoke_command_remote_positional_target_array.json @@ -281,7 +281,52 @@ "sourceLength": 86 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server2" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -325,7 +370,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -353,7 +411,20 @@ "sourceLength": 86 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "host.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ 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 94b0c73..3dd1eab 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 @@ -60,7 +60,7 @@ "sourceLength": 29 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -72,37 +72,35 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/out.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } }, { "redirectIndex": 1, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/err.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 96e8699..7238dc3 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 @@ -57,7 +57,7 @@ "sourceLength": 16 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -69,18 +69,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 0781825..47fed7f 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 @@ -57,7 +57,7 @@ "sourceLength": 13 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -69,18 +69,11 @@ "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorDuplicateRedirectAnalysis", "sourceKind": "PowerShellAllStreams", "sourceDescriptor": null, - "operation": "DescriptorDuplicate", - "targetDescriptor": 1, - "target": { - "kind": "Unknown", - "values": [], - "pattern": null, - "coveringDirectory": null - }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } 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 b0c24e3..e248ef8 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 @@ -63,7 +63,20 @@ "sourceLength": 14 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "-" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -75,18 +88,11 @@ "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorDuplicateRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "DescriptorDuplicate", - "targetDescriptor": 1, - "target": { - "kind": "Unknown", - "values": [], - "pattern": null, - "coveringDirectory": null - }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/398_v03_redirect_foreach_finite_target.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/398_v03_redirect_foreach_finite_target.json index a5e7831..932f586 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/398_v03_redirect_foreach_finite_target.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/398_v03_redirect_foreach_finite_target.json @@ -118,7 +118,20 @@ "sourceLength": 21 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "x" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -130,11 +143,12 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "FiniteSet", "values": [ "C:/work/one.txt", @@ -142,9 +156,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/403_v03_redirect_braced_null_sink.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/403_v03_redirect_braced_null_sink.json index b2239b9..7208d69 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/403_v03_redirect_braced_null_sink.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/403_v03_redirect_braced_null_sink.json @@ -57,7 +57,7 @@ "sourceLength": 18 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -69,17 +69,9 @@ "redirects": [ { "redirectIndex": 0, - "sourceKind": "Default", + "kind": "UnresolvedRedirectAnalysis", + "sourceKind": "Unknown", "sourceDescriptor": null, - "operation": "Unknown", - "targetDescriptor": null, - "target": { - "kind": "Unknown", - "values": [], - "pattern": null, - "coveringDirectory": null - }, - "isPathRelevant": false, "isComplete": false } ] 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 2690153..486c62e 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 @@ -60,7 +60,7 @@ "sourceLength": 30 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -72,37 +72,35 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "PowerShellAllStreams", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/all.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } }, { "redirectIndex": 1, + "kind": "FileRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/err.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 f94f6df..ef3f32d 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 @@ -131,7 +131,20 @@ "sourceLength": 62 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "C:\\maybe" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -175,7 +188,7 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Unknown", "values": [], @@ -185,18 +198,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 d2c280c..27f99b3 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 @@ -131,7 +131,20 @@ "sourceLength": 83 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "C:\\maybe" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -175,7 +188,7 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Unknown", "values": [], @@ -185,18 +198,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 3bc7b4e..ad575cd 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 @@ -149,7 +149,7 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Unknown", "values": [], @@ -159,18 +159,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 27919ca..4fa07aa 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 @@ -124,7 +124,20 @@ "sourceLength": 31 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "x" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -134,18 +147,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 b958226..9dc4589 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 @@ -117,7 +117,20 @@ "sourceLength": 31 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "x" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -127,20 +140,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/fixed.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 2af3856..7e02e1a 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 @@ -149,7 +149,7 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -161,11 +161,12 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "FiniteSet", "values": [ "C:/work/one.txt", @@ -173,9 +174,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 fa35be9..9c6f260 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 @@ -149,7 +149,7 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -161,11 +161,12 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "FiniteSet", "values": [ "C:/work/one.txt", @@ -173,9 +174,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 9419900..ffb441c 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 @@ -185,7 +185,7 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -197,11 +197,12 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "FiniteSet", "values": [ "C:/work/one.txt", @@ -209,9 +210,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 35cc4f1..8bc6b12 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 @@ -185,7 +185,7 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -197,11 +197,12 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "FiniteSet", "values": [ "C:/work/one.txt", @@ -209,9 +210,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/418_v03_script_block_proved_data.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/418_v03_script_block_proved_data.json index c1cb51c..99366a2 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/418_v03_script_block_proved_data.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/418_v03_script_block_proved_data.json @@ -58,7 +58,18 @@ "sourceLength": 39 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/419_v03_script_block_unknown_receiver.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/419_v03_script_block_unknown_receiver.json index 2f745bc..d6e4809 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/419_v03_script_block_unknown_receiver.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/419_v03_script_block_unknown_receiver.json @@ -163,7 +163,18 @@ "sourceLength": 40 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -200,7 +211,20 @@ "sourceLength": 24 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "target.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/420_v03_invoke_command_local_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/420_v03_invoke_command_local_region.json index 109db4c..0d0ad75 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/420_v03_invoke_command_local_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/420_v03_invoke_command_local_region.json @@ -160,7 +160,18 @@ "sourceLength": 41 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -197,7 +208,20 @@ "sourceLength": 24 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "secret.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/421_v03_module_qualified_data_exact_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/421_v03_module_qualified_data_exact_alias.json index a084b0d..dafd6ae 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/421_v03_module_qualified_data_exact_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/421_v03_module_qualified_data_exact_alias.json @@ -249,7 +249,32 @@ "sourceLength": 138 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Microsoft.PowerShell.Utility\\Write-Output" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Invoke-Command" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -279,7 +304,18 @@ "sourceLength": 138 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -323,7 +359,20 @@ "sourceLength": 24 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "victim.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/422_v03_data_receiver_canonical_alias_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/422_v03_data_receiver_canonical_alias_mutation.json index 8de4328..626c3e2 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/422_v03_data_receiver_canonical_alias_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/422_v03_data_receiver_canonical_alias_mutation.json @@ -248,7 +248,32 @@ "sourceLength": 70 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Write-Output" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Invoke-Command" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -278,7 +303,18 @@ "sourceLength": 70 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -322,7 +358,20 @@ "sourceLength": 24 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "victim.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/423_v03_design_static_stream_merge.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/423_v03_design_static_stream_merge.json index c725de7..f9438c5 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/423_v03_design_static_stream_merge.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/423_v03_design_static_stream_merge.json @@ -82,7 +82,7 @@ "sourceLength": 18 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -94,18 +94,11 @@ "redirects": [ { "redirectIndex": 0, + "kind": "DescriptorDuplicateRedirectAnalysis", "sourceKind": "Descriptor", "sourceDescriptor": 2, - "operation": "DescriptorDuplicate", - "targetDescriptor": 1, - "target": { - "kind": "Unknown", - "values": [], - "pattern": null, - "coveringDirectory": null - }, - "isPathRelevant": false, - "isComplete": true + "isComplete": true, + "targetDescriptor": 1 } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/424_v03_design_contextual_foreach_argument.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/424_v03_design_contextual_foreach_argument.json index 3778324..f3662b0 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/424_v03_design_contextual_foreach_argument.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/424_v03_design_contextual_foreach_argument.json @@ -82,7 +82,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "foreach" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/425_v03_design_escaped_home_adjacent_native_fragment.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/425_v03_design_escaped_home_adjacent_native_fragment.json index 3aa6589..8f71dad 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/425_v03_design_escaped_home_adjacent_native_fragment.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/425_v03_design_escaped_home_adjacent_native_fragment.json @@ -105,8 +105,21 @@ "sourceLength": 54 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--data" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 1, "value": { "kind": "Exact", @@ -116,6 +129,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid/api" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/426_v03_design_double_quoted_escaped_home_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/426_v03_design_double_quoted_escaped_home_path.json index c9383b6..785ab8b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/426_v03_design_double_quoted_escaped_home_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/426_v03_design_double_quoted_escaped_home_path.json @@ -84,8 +84,9 @@ "sourceLength": 24 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/427_v03_design_double_quoted_literal_expandable_composition.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/427_v03_design_double_quoted_literal_expandable_composition.json index 4e38495..03e7680 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/427_v03_design_double_quoted_literal_expandable_composition.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/427_v03_design_double_quoted_literal_expandable_composition.json @@ -82,8 +82,9 @@ "sourceLength": 29 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/428_v03_design_runtime_caret_parameter_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/428_v03_design_runtime_caret_parameter_path.json index 23b459c..f52c96b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/428_v03_design_runtime_caret_parameter_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/428_v03_design_runtime_caret_parameter_path.json @@ -82,8 +82,9 @@ "sourceLength": 16 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/429_v03_design_runtime_dollar_parameter_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/429_v03_design_runtime_dollar_parameter_path.json index 3ba95a1..845d4b0 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/429_v03_design_runtime_dollar_parameter_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/429_v03_design_runtime_dollar_parameter_path.json @@ -82,8 +82,9 @@ "sourceLength": 16 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/430_v03_design_cmdlet_quoted_tilde_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/430_v03_design_cmdlet_quoted_tilde_path.json index 103b190..2b3b919 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/430_v03_design_cmdlet_quoted_tilde_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/430_v03_design_cmdlet_quoted_tilde_path.json @@ -84,8 +84,9 @@ "sourceLength": 15 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/431_v03_design_native_quoted_tilde_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/431_v03_design_native_quoted_tilde_path.json index 93231a3..4ed673a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/431_v03_design_native_quoted_tilde_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/431_v03_design_native_quoted_tilde_path.json @@ -116,8 +116,21 @@ "sourceLength": 41 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--output" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", @@ -127,6 +140,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/432_v03_design_cmdlet_filesystem_provider_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/432_v03_design_cmdlet_filesystem_provider_path.json index af8e7fe..8259b14 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/432_v03_design_cmdlet_filesystem_provider_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/432_v03_design_cmdlet_filesystem_provider_path.json @@ -84,8 +84,9 @@ "sourceLength": 39 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/433_v03_design_native_provider_looking_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/433_v03_design_native_provider_looking_path.json index 9a10405..9556910 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/433_v03_design_native_provider_looking_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/433_v03_design_native_provider_looking_path.json @@ -116,8 +116,21 @@ "sourceLength": 62 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--output" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", @@ -127,6 +140,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/434_v03_design_cmdlet_nonfilesystem_psdrive.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/434_v03_design_cmdlet_nonfilesystem_psdrive.json index cbd5928..7d5b4d9 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/434_v03_design_cmdlet_nonfilesystem_psdrive.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/434_v03_design_cmdlet_nonfilesystem_psdrive.json @@ -82,8 +82,9 @@ "sourceLength": 23 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/435_v03_design_cmdlet_literalpath_quoted_wildcard.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/435_v03_design_cmdlet_literalpath_quoted_wildcard.json index 723cc70..71212d3 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/435_v03_design_cmdlet_literalpath_quoted_wildcard.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/435_v03_design_cmdlet_literalpath_quoted_wildcard.json @@ -100,8 +100,21 @@ "sourceLength": 32 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-LiteralPath" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/436_v03_design_native_quoted_wildcard_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/436_v03_design_native_quoted_wildcard_path.json index 7aa3193..b0044ab 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/436_v03_design_native_quoted_wildcard_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/436_v03_design_native_quoted_wildcard_path.json @@ -116,8 +116,21 @@ "sourceLength": 45 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--output" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", @@ -127,6 +140,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/437_v03_design_native_unquoted_tilde_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/437_v03_design_native_unquoted_tilde_path.json index 4117493..caf980a 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/437_v03_design_native_unquoted_tilde_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/437_v03_design_native_unquoted_tilde_path.json @@ -116,8 +116,21 @@ "sourceLength": 39 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--output" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", @@ -127,6 +140,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/438_v03_design_native_unquoted_wildcard_path.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/438_v03_design_native_unquoted_wildcard_path.json index 7d2c27c..224d81e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/438_v03_design_native_unquoted_wildcard_path.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/438_v03_design_native_unquoted_wildcard_path.json @@ -114,8 +114,21 @@ "sourceLength": 43 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--output" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", @@ -123,6 +136,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/439_v03_design_cmdlet_literalpath_quoted_tilde.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/439_v03_design_cmdlet_literalpath_quoted_tilde.json index 01f71dd..53aee8b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/439_v03_design_cmdlet_literalpath_quoted_tilde.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/439_v03_design_cmdlet_literalpath_quoted_tilde.json @@ -100,8 +100,21 @@ "sourceLength": 28 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-LiteralPath" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/440_v03_design_cmdlet_literalpath_provider.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/440_v03_design_cmdlet_literalpath_provider.json index f9e54a9..7c8a0ed 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/440_v03_design_cmdlet_literalpath_provider.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/440_v03_design_cmdlet_literalpath_provider.json @@ -100,8 +100,21 @@ "sourceLength": 52 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-LiteralPath" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/441_v03_design_redirect_quoted_tilde.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/441_v03_design_redirect_quoted_tilde.json index 532ad33..845dbb7 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/441_v03_design_redirect_quoted_tilde.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/441_v03_design_redirect_quoted_tilde.json @@ -99,7 +99,20 @@ "sourceLength": 21 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "ok" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -111,20 +124,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/Users/user" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/442_v03_design_redirect_quoted_wildcard.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/442_v03_design_redirect_quoted_wildcard.json index 1f90b1b..6292407 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/442_v03_design_redirect_quoted_wildcard.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/442_v03_design_redirect_quoted_wildcard.json @@ -99,7 +99,20 @@ "sourceLength": 25 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "ok" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -111,18 +124,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/443_v03_design_redirect_filesystem_provider.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/443_v03_design_redirect_filesystem_provider.json index 950bb44..7f216a1 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/443_v03_design_redirect_filesystem_provider.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/443_v03_design_redirect_filesystem_provider.json @@ -99,7 +99,20 @@ "sourceLength": 45 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "ok" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -111,20 +124,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/logs/x.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/444_v03_design_redirect_unknown_psdrive.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/444_v03_design_redirect_unknown_psdrive.json index f81a5aa..35ac293 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/444_v03_design_redirect_unknown_psdrive.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/444_v03_design_redirect_unknown_psdrive.json @@ -99,7 +99,20 @@ "sourceLength": 33 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "ok" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -111,18 +124,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/445_v03_cmdlet_unquoted_tilde_argument.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/445_v03_cmdlet_unquoted_tilde_argument.json index e8dc52e..598db9f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/445_v03_cmdlet_unquoted_tilde_argument.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/445_v03_cmdlet_unquoted_tilde_argument.json @@ -84,8 +84,9 @@ "sourceLength": 13 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/446_v03_cmdlet_path_unquoted_wildcard_argument.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/446_v03_cmdlet_path_unquoted_wildcard_argument.json index 1e107ee..6c3e4e8 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/446_v03_cmdlet_path_unquoted_wildcard_argument.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/446_v03_cmdlet_path_unquoted_wildcard_argument.json @@ -98,8 +98,21 @@ "sourceLength": 23 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Path" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/447_v03_cmdlet_literalpath_unquoted_wildcard_argument.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/447_v03_cmdlet_literalpath_unquoted_wildcard_argument.json index bf6c138..1151848 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/447_v03_cmdlet_literalpath_unquoted_wildcard_argument.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/447_v03_cmdlet_literalpath_unquoted_wildcard_argument.json @@ -100,8 +100,21 @@ "sourceLength": 30 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-LiteralPath" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/448_v03_native_inline_tilde_argument.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/448_v03_native_inline_tilde_argument.json index 60dc7ed..f91efaf 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/448_v03_native_inline_tilde_argument.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/448_v03_native_inline_tilde_argument.json @@ -105,8 +105,21 @@ "sourceLength": 39 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--output" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 1, "value": { "kind": "Exact", @@ -116,6 +129,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/449_v03_automatic_home_mutation_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/449_v03_automatic_home_mutation_unknown.json index a19ec8e..9a7bcde 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/449_v03_automatic_home_mutation_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/449_v03_automatic_home_mutation_unknown.json @@ -183,7 +183,44 @@ "sourceLength": 49 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Force" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -213,8 +250,9 @@ "sourceLength": 49 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/450_v03_automatic_home_mutation_preserves_native_tilde.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/450_v03_automatic_home_mutation_preserves_native_tilde.json index c9bf3da..71d00ea 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/450_v03_automatic_home_mutation_preserves_native_tilde.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/450_v03_automatic_home_mutation_preserves_native_tilde.json @@ -215,7 +215,44 @@ "sourceLength": 67 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Force" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -245,8 +282,21 @@ "sourceLength": 67 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "--output" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Exact", @@ -256,6 +306,18 @@ "pattern": null, "coveringDirectory": null } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "https://example.invalid" + ], + "pattern": null, + "coveringDirectory": null + } } ], "workingDirectory": { diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/451_v03_child_process_home_after_environment_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/451_v03_child_process_home_after_environment_mutation.json index 6712295..2770e50 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/451_v03_child_process_home_after_environment_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/451_v03_child_process_home_after_environment_mutation.json @@ -243,7 +243,32 @@ "sourceLength": 56 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -273,7 +298,18 @@ "sourceLength": 56 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -317,8 +353,9 @@ "sourceLength": 23 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/452_v03_external_script_invalidates_automatic_home.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/452_v03_external_script_invalidates_automatic_home.json index ed01a6c..ff679e9 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/452_v03_external_script_invalidates_automatic_home.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/452_v03_external_script_invalidates_automatic_home.json @@ -134,7 +134,7 @@ "sourceLength": 35 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -164,8 +164,9 @@ "sourceLength": 35 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/453_v03_explicit_hyphenated_native_path_tilde.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/453_v03_explicit_hyphenated_native_path_tilde.json index 18b3f4a..95c6d63 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/453_v03_explicit_hyphenated_native_path_tilde.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/453_v03_explicit_hyphenated_native_path_tilde.json @@ -84,8 +84,9 @@ "sourceLength": 18 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/454_v03_ambiguous_hyphenated_command_tilde.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/454_v03_ambiguous_hyphenated_command_tilde.json index d2e6bff..9205204 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/454_v03_ambiguous_hyphenated_command_tilde.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/454_v03_ambiguous_hyphenated_command_tilde.json @@ -84,8 +84,9 @@ "sourceLength": 9 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/455_v03_ambiguous_hyphenated_command_wildcard.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/455_v03_ambiguous_hyphenated_command_wildcard.json index 534050b..d763bea 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/455_v03_ambiguous_hyphenated_command_wildcard.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/455_v03_ambiguous_hyphenated_command_wildcard.json @@ -82,8 +82,9 @@ "sourceLength": 20 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/456_v03_relative_variable_provider_home_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/456_v03_relative_variable_provider_home_mutation.json index 4f2fa49..383bcb0 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/456_v03_relative_variable_provider_home_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/456_v03_relative_variable_provider_home_mutation.json @@ -244,7 +244,20 @@ "sourceLength": 69 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Variable:" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -274,7 +287,44 @@ "sourceLength": 69 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Force" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -302,8 +352,9 @@ "sourceLength": 69 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/457_v03_redirect_after_automatic_home_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/457_v03_redirect_after_automatic_home_mutation.json index a79af5d..29760db 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/457_v03_redirect_after_automatic_home_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/457_v03_redirect_after_automatic_home_mutation.json @@ -198,7 +198,44 @@ "sourceLength": 57 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Force" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -228,7 +265,20 @@ "sourceLength": 57 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -240,18 +290,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/458_v03_redirect_after_userprofile_environment_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/458_v03_redirect_after_userprofile_environment_mutation.json index 42d1f80..4f68303 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/458_v03_redirect_after_userprofile_environment_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/458_v03_redirect_after_userprofile_environment_mutation.json @@ -182,7 +182,32 @@ "sourceLength": 68 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:USERPROFILE" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -212,7 +237,20 @@ "sourceLength": 68 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -224,18 +262,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 ef880c6..c1ed999 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 @@ -207,7 +207,42 @@ "sourceLength": 69 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -244,7 +279,20 @@ "sourceLength": 31 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -254,18 +302,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 5611ed9..9339845 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 @@ -207,7 +207,42 @@ "sourceLength": 61 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -244,7 +279,20 @@ "sourceLength": 23 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -254,18 +302,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/461_v03_child_redirect_tilde_after_environment_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/461_v03_child_redirect_tilde_after_environment_mutation.json index 051f1ac..d076dc4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/461_v03_child_redirect_tilde_after_environment_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/461_v03_child_redirect_tilde_after_environment_mutation.json @@ -258,7 +258,32 @@ "sourceLength": 56 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -288,7 +313,18 @@ "sourceLength": 56 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -332,7 +368,20 @@ "sourceLength": 23 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -342,18 +391,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/462_v03_external_script_invalidates_userprofile_environment.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/462_v03_external_script_invalidates_userprofile_environment.json index 27c876f..537cafc 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/462_v03_external_script_invalidates_userprofile_environment.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/462_v03_external_script_invalidates_userprofile_environment.json @@ -134,7 +134,7 @@ "sourceLength": 50 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -164,8 +164,9 @@ "sourceLength": 50 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/463_v03_external_script_invalidates_child_home.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/463_v03_external_script_invalidates_child_home.json index bb3272f..2c80fb1 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/463_v03_external_script_invalidates_child_home.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/463_v03_external_script_invalidates_child_home.json @@ -210,7 +210,7 @@ "sourceLength": 53 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -240,7 +240,18 @@ "sourceLength": 53 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -282,8 +293,9 @@ "sourceLength": 23 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/464_v03_external_script_invalidates_location_and_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/464_v03_external_script_invalidates_location_and_binding.json index 7a95149..c9751e6 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/464_v03_external_script_invalidates_location_and_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/464_v03_external_script_invalidates_location_and_binding.json @@ -187,7 +187,7 @@ "sourceLength": 46 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -217,7 +217,20 @@ "sourceLength": 46 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "relative.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -245,8 +258,9 @@ "sourceLength": 46 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", 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 4675b4d..77c8f98 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 @@ -297,7 +297,32 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -341,7 +366,18 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -399,7 +435,20 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -409,18 +458,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 28e704f..477fcf7 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 @@ -245,7 +245,42 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -296,7 +331,20 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], @@ -306,18 +354,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 a7172ca..15d65ac 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 @@ -236,7 +236,44 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Force" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -280,7 +317,20 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -292,18 +342,17 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Unknown", "values": [], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 51cc0ee..08404f1 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 @@ -119,8 +119,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", 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 ade2115..1b3abfd 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 @@ -121,8 +121,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", 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 137d90e..11ae02f 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 @@ -206,7 +206,44 @@ "sourceLength": 105 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Force" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -250,8 +287,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/471_v03_encoded_wrapper_home_after_parent_environment_mutation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/471_v03_encoded_wrapper_home_after_parent_environment_mutation.json index c93f21c..8ee3463 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/471_v03_encoded_wrapper_home_after_parent_environment_mutation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/471_v03_encoded_wrapper_home_after_parent_environment_mutation.json @@ -190,7 +190,32 @@ "sourceLength": 98 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Env:HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -234,8 +259,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", 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 338a70c..041589c 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 @@ -221,7 +221,44 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "HOME" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "X" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Force" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -265,8 +302,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", 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 6ab7e7e..ef772dc 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 @@ -249,7 +249,20 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "/tmp/inner" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -307,7 +320,20 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -319,20 +345,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/relative.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } 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 4a35460..101390c 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 @@ -121,8 +121,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", 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 d670fa4..62aba5e 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 @@ -121,8 +121,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/476_v03_unqualified_ps1_alias_binding_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/476_v03_unqualified_ps1_alias_binding_unknown.json index 2b982bd..069df77 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/476_v03_unqualified_ps1_alias_binding_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/476_v03_unqualified_ps1_alias_binding_unknown.json @@ -167,7 +167,32 @@ "sourceLength": 41 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "foo.ps1" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Write-Output" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -197,8 +222,9 @@ "sourceLength": 41 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", 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 644a228..cba1c02 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 @@ -121,8 +121,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/478_v03_rebound_state_mutator_invalidates_all_state.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/478_v03_rebound_state_mutator_invalidates_all_state.json index 46ccf36..f01f186 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/478_v03_rebound_state_mutator_invalidates_all_state.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/478_v03_rebound_state_mutator_invalidates_all_state.json @@ -216,7 +216,32 @@ "sourceLength": 92 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Set-Variable" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Invoke-Expression" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -246,7 +271,20 @@ "sourceLength": 92 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Set-Alias evil Write-Output" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -276,8 +314,9 @@ "sourceLength": 92 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", 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 9433866..33b6d26 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 @@ -83,8 +83,9 @@ "sourceLength": 22 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/480_v03_isolated_initial_automatic_home.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/480_v03_isolated_initial_automatic_home.json index d71bb44..6dc5390 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/480_v03_isolated_initial_automatic_home.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/480_v03_isolated_initial_automatic_home.json @@ -84,8 +84,9 @@ "sourceLength": 22 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", 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 cd6b7b3..6bc7b68 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 @@ -83,8 +83,9 @@ "sourceLength": 33 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/482_v03_isolated_initial_userprofile_environment.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/482_v03_isolated_initial_userprofile_environment.json index e94dd75..a6719f4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/482_v03_isolated_initial_userprofile_environment.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/482_v03_isolated_initial_userprofile_environment.json @@ -84,8 +84,9 @@ "sourceLength": 33 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/483_v03_path_native_alias_binding_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/483_v03_path_native_alias_binding_unknown.json index 203452c..2aff2f3 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/483_v03_path_native_alias_binding_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/483_v03_path_native_alias_binding_unknown.json @@ -169,7 +169,32 @@ "sourceLength": 61 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "/usr/bin/my-tool" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Write-Output" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -199,8 +224,9 @@ "sourceLength": 61 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/484_v03_path_script_alias_binding_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/484_v03_path_script_alias_binding_unknown.json index 7b97ed5..d08bd53 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/484_v03_path_script_alias_binding_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/484_v03_path_script_alias_binding_unknown.json @@ -169,7 +169,32 @@ "sourceLength": 49 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "./tool.ps1" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "Write-Output" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -199,8 +224,9 @@ "sourceLength": 49 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", 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 072bb59..0513df2 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 @@ -121,8 +121,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", 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 285d99f..933db23 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 @@ -133,7 +133,7 @@ "sourceLength": 39 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -163,7 +163,20 @@ "sourceLength": 39 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "relative.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ 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 e6557b7..81aae5b 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 @@ -187,7 +187,7 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -231,7 +231,20 @@ "sourceLength": null } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "hi" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -243,20 +256,19 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "Exact", "values": [ "C:/work/relative.txt" ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/489_v03_iex_cmdlet_tilde_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/489_v03_iex_cmdlet_tilde_binding.json index 352de36..ca16332 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/489_v03_iex_cmdlet_tilde_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/489_v03_iex_cmdlet_tilde_binding.json @@ -121,8 +121,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/490_v03_iex_cmdlet_wildcard_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/490_v03_iex_cmdlet_wildcard_binding.json index ce9b9c0..5cef1fa 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/490_v03_iex_cmdlet_wildcard_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/490_v03_iex_cmdlet_wildcard_binding.json @@ -119,8 +119,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/491_v03_iex_native_tilde_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/491_v03_iex_native_tilde_binding.json index 6fdd348..251e399 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/491_v03_iex_native_tilde_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/491_v03_iex_native_tilde_binding.json @@ -119,8 +119,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/493_v03_windows_powershell_curl_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/493_v03_windows_powershell_curl_alias.json index 074117c..24cf103 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/493_v03_windows_powershell_curl_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/493_v03_windows_powershell_curl_alias.json @@ -85,7 +85,20 @@ "sourceLength": 17 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "example.test" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/494_v03_windows_powershell_parallel_receiver_unknown.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/494_v03_windows_powershell_parallel_receiver_unknown.json index 243436d..65c5a7b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/494_v03_windows_powershell_parallel_receiver_unknown.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/494_v03_windows_powershell_parallel_receiver_unknown.json @@ -161,7 +161,30 @@ "sourceLength": 37 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Parallel" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -198,7 +221,7 @@ "sourceLength": 10 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/495_v03_windows_powershell_wmi_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/495_v03_windows_powershell_wmi_alias.json index f84ff7b..5db5cb8 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/495_v03_windows_powershell_wmi_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/495_v03_windows_powershell_wmi_alias.json @@ -85,7 +85,20 @@ "sourceLength": 26 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Win32_OperatingSystem" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/496_v03_windows_powershell_no_get_error_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/496_v03_windows_powershell_no_get_error_alias.json index 4278f75..361d305 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/496_v03_windows_powershell_no_get_error_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/496_v03_windows_powershell_no_get_error_alias.json @@ -65,7 +65,7 @@ "sourceLength": 4 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/504_v03_windows_powershell_convert_from_string_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/504_v03_windows_powershell_convert_from_string_alias.json index 427f779..1368389 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/504_v03_windows_powershell_convert_from_string_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/504_v03_windows_powershell_convert_from_string_alias.json @@ -66,7 +66,7 @@ "sourceLength": 3 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/505_v03_design_simple_command_substitution.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/505_v03_design_simple_command_substitution.json index 0037920..63ffe62 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/505_v03_design_simple_command_substitution.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/505_v03_design_simple_command_substitution.json @@ -169,7 +169,20 @@ "sourceLength": 19 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "target.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -192,8 +205,9 @@ "sourceLength": 34 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/506_v03_design_literal_substitution_spellings.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/506_v03_design_literal_substitution_spellings.json index af21c05..1157270 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/506_v03_design_literal_substitution_spellings.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/506_v03_design_literal_substitution_spellings.json @@ -98,7 +98,32 @@ "sourceLength": 53 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "$(Get-Date)" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "literal $(Get-Location)" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/507_v03_design_call_operator_script_block_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/507_v03_design_call_operator_script_block_region.json index 3ff036b..8409f89 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/507_v03_design_call_operator_script_block_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/507_v03_design_call_operator_script_block_region.json @@ -125,7 +125,20 @@ "sourceLength": 24 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "target.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/508_v03_design_call_operator_subexpression_invocation.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/508_v03_design_call_operator_subexpression_invocation.json index 7b3fe31..53fedd3 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/508_v03_design_call_operator_subexpression_invocation.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/508_v03_design_call_operator_subexpression_invocation.json @@ -151,7 +151,20 @@ "sourceLength": 21 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "Get-Date" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -174,7 +187,7 @@ "sourceLength": 26 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/509_v03_design_command_substitution_current_scope_cwd.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/509_v03_design_command_substitution_current_scope_cwd.json index d06e62a..dd2edfc 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/509_v03_design_command_substitution_current_scope_cwd.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/509_v03_design_command_substitution_current_scope_cwd.json @@ -306,7 +306,20 @@ "sourceLength": 33 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "/tmp" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -357,7 +370,7 @@ "sourceLength": 33 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -387,8 +400,9 @@ "sourceLength": 72 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", @@ -425,7 +439,20 @@ "sourceLength": 72 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "relative.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/510_v03_design_foreach_subexpression_iterator.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/510_v03_design_foreach_subexpression_iterator.json index d22aac2..c1d87d2 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/510_v03_design_foreach_subexpression_iterator.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/510_v03_design_foreach_subexpression_iterator.json @@ -239,7 +239,20 @@ "sourceLength": 22 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "C:\\input" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -276,8 +289,21 @@ "sourceLength": 29 } ], - "effectiveArguments": [ + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-LiteralPath" + ], + "pattern": null, + "coveringDirectory": null + } + }, { + "clauseArgumentIndex": 1, "clauseElementIndex": 2, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/511_v03_design_foreach_cmdlet_parameter_like_value.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/511_v03_design_foreach_cmdlet_parameter_like_value.json index 13b27b8..342fa76 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/511_v03_design_foreach_cmdlet_parameter_like_value.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/511_v03_design_foreach_cmdlet_parameter_like_value.json @@ -136,8 +136,9 @@ "sourceLength": 16 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/512_v03_design_foreach_native_option_like_value.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/512_v03_design_foreach_native_option_like_value.json index 247cfa6..9200b31 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/512_v03_design_foreach_native_option_like_value.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/512_v03_design_foreach_native_option_like_value.json @@ -136,8 +136,9 @@ "sourceLength": 9 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/513_v03_design_nested_foreach.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/513_v03_design_nested_foreach.json index e00469d..7acf698 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/513_v03_design_nested_foreach.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/513_v03_design_nested_foreach.json @@ -190,8 +190,9 @@ "sourceLength": 22 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/515_v03_design_foreach_candidate_cap_32.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/515_v03_design_foreach_candidate_cap_32.json index fd6e68c..6121b52 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/515_v03_design_foreach_candidate_cap_32.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/515_v03_design_foreach_candidate_cap_32.json @@ -136,8 +136,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/516_v03_design_variable_writer_invalidates_proved_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/516_v03_design_variable_writer_invalidates_proved_binding.json index 9d1b7ea..c743d42 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/516_v03_design_variable_writer_invalidates_proved_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/516_v03_design_variable_writer_invalidates_proved_binding.json @@ -223,7 +223,44 @@ "sourceLength": 92 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "C:/sensitive.txt" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "-OutVariable" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "f" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -253,8 +290,9 @@ "sourceLength": 92 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/519_v03_design_dynamic_invoke_expression_invalidates_current_state.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/519_v03_design_dynamic_invoke_expression_invalidates_current_state.json index 5c03894..8a85241 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/519_v03_design_dynamic_invoke_expression_invalidates_current_state.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/519_v03_design_dynamic_invoke_expression_invalidates_current_state.json @@ -195,8 +195,9 @@ "sourceLength": 63 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", @@ -235,8 +236,9 @@ "sourceLength": 63 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/520_v03_design_foreach_binding_is_case_insensitive.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/520_v03_design_foreach_binding_is_case_insensitive.json index 948017d..6e9ad9d 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/520_v03_design_foreach_binding_is_case_insensitive.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/520_v03_design_foreach_binding_is_case_insensitive.json @@ -136,8 +136,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/521_v03_design_nested_same_name_foreach_overwrites_binding.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/521_v03_design_nested_same_name_foreach_overwrites_binding.json index 59f362f..82a8e7f 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/521_v03_design_nested_same_name_foreach_overwrites_binding.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/521_v03_design_nested_same_name_foreach_overwrites_binding.json @@ -324,8 +324,9 @@ "sourceLength": 17 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", @@ -387,8 +388,9 @@ "sourceLength": 63 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", @@ -429,8 +431,9 @@ "sourceLength": 111 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Exact", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/522_v03_design_foreach_binding_used_as_redirect_target.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/522_v03_design_foreach_binding_used_as_redirect_target.json index 8b9812d..721283b 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/522_v03_design_foreach_binding_used_as_redirect_target.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/522_v03_design_foreach_binding_used_as_redirect_target.json @@ -153,7 +153,20 @@ "sourceLength": 27 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "payload" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -165,11 +178,12 @@ "redirects": [ { "redirectIndex": 0, + "kind": "FileRedirectAnalysis", "sourceKind": "Default", "sourceDescriptor": null, - "operation": "FileOutput", - "targetDescriptor": null, - "target": { + "isComplete": true, + "fileMode": "Output", + "value": { "kind": "FiniteSet", "values": [ "C:/work/a.log", @@ -177,9 +191,7 @@ ], "pattern": null, "coveringDirectory": null - }, - "isPathRelevant": true, - "isComplete": true + } } ] } diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/523_v03_design_foreach_current_runspace_invoke_expression.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/523_v03_design_foreach_current_runspace_invoke_expression.json index 0aa352f..b558ff5 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/523_v03_design_foreach_current_runspace_invoke_expression.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/523_v03_design_foreach_current_runspace_invoke_expression.json @@ -173,8 +173,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/524_v03_design_foreach_child_host_does_not_inherit_state_mode.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/524_v03_design_foreach_child_host_does_not_inherit_state_mode.json index 44fb741..1924cd0 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/524_v03_design_foreach_child_host_does_not_inherit_state_mode.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/524_v03_design_foreach_child_host_does_not_inherit_state_mode.json @@ -173,8 +173,9 @@ "sourceLength": null } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/525_v03_design_dot_source_script_block_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/525_v03_design_dot_source_script_block_region.json index 41bb31a..d4b013e 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/525_v03_design_dot_source_script_block_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/525_v03_design_dot_source_script_block_region.json @@ -106,7 +106,7 @@ "sourceLength": 14 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/526_v03_design_pipeline_foreach_alias.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/526_v03_design_pipeline_foreach_alias.json index d8f5c10..641e7f9 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/526_v03_design_pipeline_foreach_alias.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/526_v03_design_pipeline_foreach_alias.json @@ -209,7 +209,7 @@ "sourceLength": 42 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -239,7 +239,18 @@ "sourceLength": 42 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -283,8 +294,9 @@ "sourceLength": 16 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/527_v03_design_where_object_filter_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/527_v03_design_where_object_filter_region.json index 8444548..8fa56e5 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/527_v03_design_where_object_filter_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/527_v03_design_where_object_filter_region.json @@ -224,7 +224,7 @@ "sourceLength": 59 } ], - "effectiveArguments": [], + "arguments": [], "workingDirectory": { "kind": "Exact", "values": [ @@ -254,7 +254,30 @@ "sourceLength": 59 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-FilterScript" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -298,8 +321,9 @@ "sourceLength": 14 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "Unknown", diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/528_v03_design_foreach_object_authored_versus_phase_order.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/528_v03_design_foreach_object_authored_versus_phase_order.json index 0e3320e..71ee185 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/528_v03_design_foreach_object_authored_versus_phase_order.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/528_v03_design_foreach_object_authored_versus_phase_order.json @@ -390,7 +390,74 @@ "sourceLength": 104 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-End" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Begin" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 4, + "clauseElementIndex": 5, + "value": { + "kind": "Exact", + "values": [ + "-Process" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 5, + "clauseElementIndex": 6, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -427,7 +494,20 @@ "sourceLength": 18 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "end" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -464,7 +544,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "begin" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -501,7 +594,20 @@ "sourceLength": 22 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "process" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/529_v03_design_invoke_command_local_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/529_v03_design_invoke_command_local_region.json index ab10d65..d5c8da6 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/529_v03_design_invoke_command_local_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/529_v03_design_invoke_command_local_region.json @@ -176,7 +176,30 @@ "sourceLength": 50 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -213,7 +236,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/530_v03_design_invoke_command_remote_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/530_v03_design_invoke_command_remote_region.json index 9c071cd..2521367 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/530_v03_design_invoke_command_remote_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/530_v03_design_invoke_command_remote_region.json @@ -212,7 +212,54 @@ "sourceLength": 71 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ComputerName" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "server" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -249,7 +296,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Unknown", "values": [], diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/531_v03_design_start_job_initialization_and_main.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/531_v03_design_start_job_initialization_and_main.json index adc1c34..ffba518 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/531_v03_design_start_job_initialization_and_main.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/531_v03_design_start_job_initialization_and_main.json @@ -282,7 +282,52 @@ "sourceLength": 88 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-InitializationScript" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -319,7 +364,20 @@ "sourceLength": 19 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "main" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -356,7 +414,20 @@ "sourceLength": 19 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "init" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/532_v03_design_new_module_initialization_region.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/532_v03_design_new_module_initialization_region.json index 4f88bfd..90603e4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/532_v03_design_new_module_initialization_region.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/532_v03_design_new_module_initialization_region.json @@ -174,7 +174,30 @@ "sourceLength": 52 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-ScriptBlock" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -211,7 +234,20 @@ "sourceLength": 26 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "initialized" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/536_v03_execution_region_measure_command.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/536_v03_execution_region_measure_command.json index 9d19e18..12e0df4 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/536_v03_execution_region_measure_command.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/536_v03_execution_region_measure_command.json @@ -160,7 +160,18 @@ "sourceLength": 38 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -197,7 +208,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/537_v03_execution_region_trace_command.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/537_v03_execution_region_trace_command.json index 7ae4df9..027e7b8 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/537_v03_execution_region_trace_command.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/537_v03_execution_region_trace_command.json @@ -224,7 +224,66 @@ "sourceLength": 79 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-Name" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Exact", + "values": [ + "ParameterBinding" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Exact", + "values": [ + "-Expression" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 4, + "clauseElementIndex": 5, + "value": { + "kind": "Exact", + "values": [ + "-PSHost" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -261,7 +320,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "child.txt" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/538_v03_execution_region_foreach_object_remaining_scripts.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/538_v03_execution_region_foreach_object_remaining_scripts.json index 81bbda1..4ca2207 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/538_v03_execution_region_foreach_object_remaining_scripts.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/538_v03_execution_region_foreach_object_remaining_scripts.json @@ -374,7 +374,62 @@ "sourceLength": 112 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "-RemainingScripts" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 1, + "clauseElementIndex": 2, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 2, + "clauseElementIndex": 3, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 3, + "clauseElementIndex": 4, + "value": { + "kind": "Exact", + "values": [ + "-Process" + ], + "pattern": null, + "coveringDirectory": null + } + }, + { + "clauseArgumentIndex": 4, + "clauseElementIndex": 5, + "value": { + "kind": "Unknown", + "values": [], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -411,7 +466,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "first" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -448,7 +516,20 @@ "sourceLength": 21 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "second" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ @@ -485,7 +566,20 @@ "sourceLength": 20 } ], - "effectiveArguments": [], + "arguments": [ + { + "clauseArgumentIndex": 0, + "clauseElementIndex": 1, + "value": { + "kind": "Exact", + "values": [ + "third" + ], + "pattern": null, + "coveringDirectory": null + } + } + ], "workingDirectory": { "kind": "Exact", "values": [ diff --git a/tests/ShellSyntaxTree.Tests/Corpus/powershell/539_v03_design_foreach_dynamic_command_identity.json b/tests/ShellSyntaxTree.Tests/Corpus/powershell/539_v03_design_foreach_dynamic_command_identity.json index 30b2524..3f09294 100644 --- a/tests/ShellSyntaxTree.Tests/Corpus/powershell/539_v03_design_foreach_dynamic_command_identity.json +++ b/tests/ShellSyntaxTree.Tests/Corpus/powershell/539_v03_design_foreach_dynamic_command_identity.json @@ -137,8 +137,9 @@ "sourceLength": 11 } ], - "effectiveArguments": [ + "arguments": [ { + "clauseArgumentIndex": 0, "clauseElementIndex": 1, "value": { "kind": "FiniteSet", diff --git a/tests/ShellSyntaxTree.Tests/Parsing/BashForInStructuralTests.cs b/tests/ShellSyntaxTree.Tests/Parsing/BashForInStructuralTests.cs index 1e88248..8a84f75 100644 --- a/tests/ShellSyntaxTree.Tests/Parsing/BashForInStructuralTests.cs +++ b/tests/ShellSyntaxTree.Tests/Parsing/BashForInStructuralTests.cs @@ -635,6 +635,19 @@ public void Empty_loop_success_continuation_keeps_exact_state() Assert.Equal("/work/relative.txt", relative.Resolved); } + [Fact] + public void Zero_or_more_loop_joins_the_zero_iteration_cwd_path() + { + var result = Parse( + "for f in /tmp/*.txt; do cd /tmp; done; pwd"); + + Assert.False(result.IsUnparseable, result.UnparseableReason); + var pwd = Assert.Single(result.Commands, command => CommandVerb(command) == "pwd"); + Assert.IsType(pwd.WorkingDirectory); + Assert.Contains(pwd.Clause.Args, argument => + argument.IsCwdAttribution && argument.Kind == ArgKind.DynamicSkip); + } + [Fact] public void Thirty_two_visits_preserve_the_last_value_after_the_loop() { @@ -1056,7 +1069,7 @@ private static ParsedCommand Parse(string input) => private static string CommandVerb(CommandOccurrence command) => command.Clause.Verb.Joined; - private static ShellValueDomain FiniteDomain(int count) => new() + private static ShellValueDomainFacts FiniteDomain(int count) => new() { Kind = ShellValueDomainKind.FiniteSet, Values = Enumerable.Range(0, count).Select(index => $"v{index}").ToArray(), diff --git a/tests/ShellSyntaxTree.Tests/Parsing/BashStructuralProjectionTests.cs b/tests/ShellSyntaxTree.Tests/Parsing/BashStructuralProjectionTests.cs index b47f277..78ae9c9 100644 --- a/tests/ShellSyntaxTree.Tests/Parsing/BashStructuralProjectionTests.cs +++ b/tests/ShellSyntaxTree.Tests/Parsing/BashStructuralProjectionTests.cs @@ -38,6 +38,25 @@ public void Simple_command_populates_all_projections_with_shared_leaf_identity() Assert.Equal(0, root.ChildIndex); } + [Fact] + public void Inline_native_option_joins_two_arguments_to_one_source_element() + { + var occurrence = Assert.Single( + Parse("git --work-tree=../repo status").Commands); + + Assert.Equal(3, occurrence.Arguments.Count); + var option = occurrence.Arguments[0]; + var operand = occurrence.Arguments[1]; + Assert.Equal("--work-tree", option.Argument.Raw); + Assert.Equal("../repo", operand.Argument.Raw); + Assert.Same(option.Element, operand.Element); + Assert.Equal("--work-tree=../repo", option.Element.Raw); + Assert.Equal("--work-tree", Assert.IsType( + option.Value).Value); + Assert.Equal("../repo", Assert.IsType( + operand.Value).Value); + } + [Fact] public void Mixed_pipeline_and_list_preserve_authored_structure_and_roles() { @@ -245,7 +264,7 @@ public void Literal_file_redirect_has_complete_explicit_analysis() var redirect = Assert.Single(command.Redirects); Assert.Equal(0, redirect.RedirectIndex); Assert.Equal(RedirectSourceKind.Default, redirect.Source.Kind); - Assert.Null(redirect.Source.Descriptor); + Assert.Null(redirect.Source.DescriptorValue); Assert.Equal(RedirectOperation.FileOutput, redirect.Operation); Assert.Equal(ShellValueDomainKind.Exact, redirect.Target.Kind); Assert.Equal("/work/out.txt", Assert.Single(redirect.Target.Values)); @@ -265,9 +284,9 @@ public void Literal_file_redirect_has_complete_explicit_analysis() [InlineData("command 10>&2-", RedirectSourceKind.Descriptor, 10, RedirectOperation.DescriptorMove, 2)] public void Literal_descriptor_redirects_are_complete_and_not_paths( string source, - RedirectSourceKind sourceKind, + object sourceKind, int? sourceDescriptor, - RedirectOperation operation, + object operation, int? targetDescriptor) { var result = Parse(source); @@ -276,9 +295,9 @@ public void Literal_descriptor_redirects_are_complete_and_not_paths( var command = Assert.Single(result.Commands); Assert.True(command.IsComplete); var redirect = Assert.Single(command.Redirects); - Assert.Equal(sourceKind, redirect.Source.Kind); - Assert.Equal(sourceDescriptor, redirect.Source.Descriptor); - Assert.Equal(operation, redirect.Operation); + Assert.Equal((RedirectSourceKind)sourceKind, redirect.Source.Kind); + Assert.Equal(sourceDescriptor, redirect.Source.DescriptorValue); + Assert.Equal((RedirectOperation)operation, redirect.Operation); Assert.Equal(targetDescriptor, redirect.TargetDescriptor); Assert.Equal(ShellValueDomainKind.Unknown, redirect.Target.Kind); Assert.False(redirect.IsPathRelevant); @@ -292,7 +311,7 @@ public void Literal_descriptor_redirects_are_complete_and_not_paths( public void Numeric_source_file_redirect_preserves_descriptor( string source, int sourceDescriptor, - RedirectOperation operation) + object operation) { var result = Parse(source); @@ -301,8 +320,8 @@ public void Numeric_source_file_redirect_preserves_descriptor( Assert.True(command.IsComplete); var redirect = Assert.Single(command.Redirects); Assert.Equal(RedirectSourceKind.Descriptor, redirect.Source.Kind); - Assert.Equal(sourceDescriptor, redirect.Source.Descriptor); - Assert.Equal(operation, redirect.Operation); + Assert.Equal(sourceDescriptor, redirect.Source.DescriptorValue); + Assert.Equal((RedirectOperation)operation, redirect.Operation); Assert.True(redirect.IsPathRelevant); Assert.True(redirect.IsComplete); } @@ -327,7 +346,7 @@ public void Quoted_adjacent_digits_are_an_argument_not_a_source_descriptor() public void Continued_numeric_source_preserves_descriptor_semantics( string source, int sourceDescriptor, - RedirectOperation operation, + object operation, int? targetDescriptor) { var result = Parse(source); @@ -338,8 +357,8 @@ public void Continued_numeric_source_preserves_descriptor_semantics( Assert.DoesNotContain(command.Clause.Args, argument => argument.Raw is "3" or "10"); var redirect = Assert.Single(command.Redirects); Assert.Equal(RedirectSourceKind.Descriptor, redirect.Source.Kind); - Assert.Equal(sourceDescriptor, redirect.Source.Descriptor); - Assert.Equal(operation, redirect.Operation); + Assert.Equal(sourceDescriptor, redirect.Source.DescriptorValue); + Assert.Equal((RedirectOperation)operation, redirect.Operation); Assert.Equal(targetDescriptor, redirect.TargetDescriptor); Assert.True(redirect.IsComplete); } @@ -402,11 +421,10 @@ public void Computed_descriptor_target_remains_incomplete(string source) Assert.False(result.IsUnparseable, result.UnparseableReason); var command = Assert.Single(result.Commands); Assert.False(command.IsComplete); - var redirect = Assert.Single(command.Redirects); - Assert.Equal(RedirectOperation.DescriptorDuplicate, redirect.Operation); - Assert.Null(redirect.TargetDescriptor); - Assert.Equal(ShellValueDomainKind.Unknown, redirect.Target.Kind); - Assert.False(redirect.IsPathRelevant); + var redirect = Assert.IsType( + Assert.Single(command.Redirects)); + Assert.NotNull(redirect.Source); + Assert.Same(command.Clause.Redirects[0], redirect.Authored); Assert.False(redirect.IsComplete); } @@ -415,7 +433,7 @@ public void Computed_descriptor_target_remains_incomplete(string source) [InlineData("command &>> out.log", RedirectOperation.CombinedOutputAppend)] public void Combined_output_redirects_have_explicit_operations( string source, - RedirectOperation operation) + object operation) { var result = Parse(source); @@ -424,7 +442,7 @@ public void Combined_output_redirects_have_explicit_operations( Assert.True(command.IsComplete); var redirect = Assert.Single(command.Redirects); Assert.Equal(RedirectSourceKind.Default, redirect.Source.Kind); - Assert.Equal(operation, redirect.Operation); + Assert.Equal((RedirectOperation)operation, redirect.Operation); Assert.Equal("/work/out.log", Assert.Single(redirect.Target.Values)); Assert.True(redirect.IsPathRelevant); Assert.True(redirect.IsComplete); @@ -1094,7 +1112,7 @@ public void Literal_heredoc_publishes_authored_data_and_retains_compatibility_re var redirect = occurrence.Redirects[1]; Assert.Equal(1, redirect.RedirectIndex); Assert.Equal(RedirectSourceKind.Default, redirect.Source.Kind); - Assert.Null(redirect.Source.Descriptor); + Assert.Null(redirect.Source.DescriptorValue); Assert.Equal(RedirectOperation.HereDocument, redirect.Operation); Assert.Equal(ShellValueDomainKind.Unknown, redirect.Target.Kind); Assert.False(redirect.IsPathRelevant); @@ -1156,7 +1174,7 @@ expectedDescriptor is null ? RedirectSourceKind.Default : RedirectSourceKind.Descriptor, redirect.Source.Kind); - Assert.Equal(expectedDescriptor, redirect.Source.Descriptor); + Assert.Equal(expectedDescriptor, redirect.Source.DescriptorValue); var compatibility = Assert.Single(occurrence.Clause.Redirects); Assert.Equal(RedirectDirection.In, compatibility.Direction); @@ -1283,7 +1301,7 @@ public void Numeric_source_heredoc_preserves_descriptor_and_body_semantics( Assert.True(occurrence.IsComplete); var redirect = Assert.Single(occurrence.Redirects); Assert.Equal(RedirectSourceKind.Descriptor, redirect.Source.Kind); - Assert.Equal(sourceDescriptor, redirect.Source.Descriptor); + Assert.Equal(sourceDescriptor, redirect.Source.DescriptorValue); Assert.Equal(RedirectOperation.HereDocument, redirect.Operation); Assert.True(redirect.IsComplete); var hereDocument = Assert.IsType(redirect.HereDocument); @@ -1299,20 +1317,18 @@ public void Numeric_source_heredoc_preserves_descriptor_and_body_semantics( } [Fact] - public void Overflow_numeric_source_heredoc_preserves_body_but_remains_incomplete() + public void Overflow_numeric_source_heredoc_is_explicitly_unresolved() { var result = Parse("cat 999999999999999999999<( + Assert.Single(occurrence.Redirects)); + Assert.IsType(redirect.Source); Assert.False(redirect.IsComplete); - var hereDocument = Assert.IsType(redirect.HereDocument); - Assert.Equal("body\n", hereDocument.Body.Raw); - Assert.True(hereDocument.IsComplete); + Assert.Same(occurrence.Clause.Redirects[0], redirect.Authored); } [Theory] diff --git a/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionStructuralTests.cs b/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionStructuralTests.cs index 8f52598..eb1bbcd 100644 --- a/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionStructuralTests.cs +++ b/tests/ShellSyntaxTree.Tests/Parsing/PwshExecutionRegionStructuralTests.cs @@ -459,7 +459,7 @@ public void Parallel_pooled_rebinding_conservatively_taints_later_activations() [InlineData(" -UseNewRunspace", ShellValueDomainKind.Exact, true)] public void Parallel_pooled_rebinding_can_escape_on_a_later_activation( string runspaceOption, - ShellValueDomainKind expectedCwdKind, + object expectedCwdKind, bool expectedComplete) { var result = ParseIsolated( @@ -471,7 +471,9 @@ public void Parallel_pooled_rebinding_can_escape_on_a_later_activation( var continuation = result.Commands.Last(); Assert.Equal("Get-Item", continuation.Clause.Verb.Tokens[0]); Assert.Equal(expectedComplete, continuation.IsComplete); - Assert.Equal(expectedCwdKind, continuation.WorkingDirectory.Kind); + Assert.Equal( + (ShellValueDomainKind)expectedCwdKind, + continuation.WorkingDirectory.Kind); } [Theory] diff --git a/tests/ShellSyntaxTree.Tests/Parsing/PwshRedirectAnalysisTests.cs b/tests/ShellSyntaxTree.Tests/Parsing/PwshRedirectAnalysisTests.cs index 2479be5..06dd4d1 100644 --- a/tests/ShellSyntaxTree.Tests/Parsing/PwshRedirectAnalysisTests.cs +++ b/tests/ShellSyntaxTree.Tests/Parsing/PwshRedirectAnalysisTests.cs @@ -21,9 +21,9 @@ public class PwshRedirectAnalysisTests [InlineData("*>>", RedirectSourceKind.PowerShellAllStreams, null, RedirectOperation.FileAppend)] public void File_redirect_preserves_source_operation_and_path( string op, - RedirectSourceKind sourceKind, + object sourceKind, int? descriptor, - RedirectOperation operation) + object operation) { var result = Parse($"Get-Date {op} out.txt"); @@ -32,9 +32,9 @@ public void File_redirect_preserves_source_operation_and_path( Assert.True(occurrence.IsComplete); var redirect = Assert.Single(occurrence.Redirects); Assert.Equal(0, redirect.RedirectIndex); - Assert.Equal(sourceKind, redirect.Source.Kind); - Assert.Equal(descriptor, redirect.Source.Descriptor); - Assert.Equal(operation, redirect.Operation); + Assert.Equal((RedirectSourceKind)sourceKind, redirect.Source.Kind); + Assert.Equal(descriptor, redirect.Source.DescriptorValue); + Assert.Equal((RedirectOperation)operation, redirect.Operation); Assert.Equal(ShellValueDomainKind.Exact, redirect.Target.Kind); Assert.Equal("C:/work/out.txt", Assert.Single(redirect.Target.Values)); Assert.True(redirect.IsPathRelevant); @@ -47,7 +47,7 @@ public void File_redirect_preserves_source_operation_and_path( [InlineData("*>&1", RedirectSourceKind.PowerShellAllStreams, null)] public void Stream_merge_preserves_source_and_success_stream_target( string op, - RedirectSourceKind sourceKind, + object sourceKind, int? sourceDescriptor) { var result = Parse($"Get-Date {op}"); @@ -56,8 +56,8 @@ public void Stream_merge_preserves_source_and_success_stream_target( var occurrence = Assert.Single(result.Commands); Assert.True(occurrence.IsComplete); var redirect = Assert.Single(occurrence.Redirects); - Assert.Equal(sourceKind, redirect.Source.Kind); - Assert.Equal(sourceDescriptor, redirect.Source.Descriptor); + Assert.Equal((RedirectSourceKind)sourceKind, redirect.Source.Kind); + Assert.Equal(sourceDescriptor, redirect.Source.DescriptorValue); Assert.Equal(RedirectOperation.DescriptorDuplicate, redirect.Operation); Assert.Equal(1, redirect.TargetDescriptor); Assert.Equal(ShellValueDomainKind.Unknown, redirect.Target.Kind); @@ -75,7 +75,7 @@ public void Multiple_redirects_preserve_authored_order_and_coordinates() Assert.Equal(new[] { 0, 1 }, redirects.Select(item => item.RedirectIndex)); Assert.Equal( new[] { 1, 2 }, - redirects.Select(item => item.Source.Descriptor ?? 1)); + redirects.Select(item => item.Source.DescriptorValue ?? 1)); Assert.Equal( new[] { "C:/work/out.txt", "C:/work/err.txt" }, redirects.Select(item => Assert.Single(item.Target.Values))); @@ -137,7 +137,7 @@ public void All_streams_and_numbered_stream_redirects_remain_independent() var redirects = Assert.Single(result.Commands).Redirects; Assert.Equal(2, redirects.Count); Assert.Equal(RedirectSourceKind.PowerShellAllStreams, redirects[0].Source.Kind); - Assert.Equal(2, redirects[1].Source.Descriptor); + Assert.Equal(2, redirects[1].Source.DescriptorValue); } [Fact] diff --git a/tests/ShellSyntaxTree.Tests/Parsing/PwshStructuralProjectionTests.cs b/tests/ShellSyntaxTree.Tests/Parsing/PwshStructuralProjectionTests.cs index ffe6961..fcb0d51 100644 --- a/tests/ShellSyntaxTree.Tests/Parsing/PwshStructuralProjectionTests.cs +++ b/tests/ShellSyntaxTree.Tests/Parsing/PwshStructuralProjectionTests.cs @@ -36,6 +36,25 @@ public void Simple_command_populates_all_projections_with_shared_leaf_identity() Assert.Equal(CommandOccurrenceRole.Ordinary, occurrence.ImmediateRole); } + [Fact] + public void Inline_cmdlet_parameter_joins_two_arguments_to_one_source_element() + { + var occurrence = Assert.Single( + Parse("Remove-Item -Path:C:\\repo").Commands); + + Assert.Equal(2, occurrence.Arguments.Count); + var parameter = occurrence.Arguments[0]; + var operand = occurrence.Arguments[1]; + Assert.Equal("-Path", parameter.Argument.Raw); + Assert.Equal("C:\\repo", operand.Argument.Raw); + Assert.Same(parameter.Element, operand.Element); + Assert.Equal("-Path:C:\\repo", parameter.Element.Raw); + Assert.Equal("-Path", Assert.IsType( + parameter.Value).Value); + Assert.Equal("C:\\repo", Assert.IsType( + operand.Value).Value); + } + [Fact] public void Mixed_pipeline_and_list_preserve_authored_structure_and_roles() { @@ -1052,7 +1071,7 @@ public void Repeated_parallel_child_does_not_reuse_home_after_process_environmen null)] public void Child_process_home_depends_on_proved_process_environment( string source, - ShellValueDomainKind expectedKind, + object expectedKind, string? expectedValue) { var result = ParseIsolatedWithHome(source, "C:/Users/test"); @@ -1061,7 +1080,7 @@ public void Child_process_home_depends_on_proved_process_environment( result.Commands, occurrence => occurrence.Clause.Verb.Tokens[0] == "Get-Content"); var effective = Assert.Single(command.EffectiveArguments); - Assert.Equal(expectedKind, effective.Value.Kind); + Assert.Equal((ShellValueDomainKind)expectedKind, effective.Value.Kind); if (expectedValue is not null) { Assert.Equal(expectedValue, Assert.Single(effective.Value.Values)); diff --git a/tests/ShellSyntaxTree.Tests/PublicApiSnapshotTests.cs b/tests/ShellSyntaxTree.Tests/PublicApiSnapshotTests.cs index f9e7c1d..5b318d2 100644 --- a/tests/ShellSyntaxTree.Tests/PublicApiSnapshotTests.cs +++ b/tests/ShellSyntaxTree.Tests/PublicApiSnapshotTests.cs @@ -581,7 +581,7 @@ public void Public_namespace_contains_only_expected_types() { var actual = LibAssembly .GetExportedTypes() - .Where(t => t.Namespace == "ShellSyntaxTree") + .Where(t => t.Namespace == "ShellSyntaxTree" && t.DeclaringType is null) .Select(t => t.Name) .OrderBy(n => n) .ToArray(); @@ -589,6 +589,7 @@ public void Public_namespace_contains_only_expected_types() var expected = new[] { nameof(Arg), + nameof(AnalyzedArgument), nameof(ArgKind), nameof(BashParser), nameof(BashInitialStateMode), @@ -604,22 +605,21 @@ public void Public_namespace_contains_only_expected_types() nameof(CommandOccurrenceRole), nameof(CommandSubstitutionSyntax), nameof(CompoundOperator), - nameof(ConditionalBranchSyntax), - nameof(ConditionalSyntax), - nameof(ConditionLoopKind), - nameof(ConditionLoopSyntax), - nameof(EffectiveArgument), + nameof(DescriptorCloseRedirectAnalysis), + nameof(DescriptorDuplicateRedirectAnalysis), + nameof(DescriptorMoveRedirectAnalysis), nameof(ExecutionRegionCardinality), nameof(ExecutionRegionOrigin), nameof(ExecutionRegionPhase), nameof(ExecutionRegionSyntax), nameof(ExecutionRegionTiming), nameof(ForEachSyntax), + nameof(FileRedirectAnalysis), + nameof(FileRedirectMode), nameof(GroupSyntax), nameof(HereDocumentAnalysis), nameof(HereDocumentExpansionMode), nameof(IShellParser), - nameof(LoopBindingSyntax), nameof(ParsedCommand), nameof(PipelineSyntax), nameof(PwshParser), @@ -629,19 +629,17 @@ public void Public_namespace_contains_only_expected_types() nameof(Redirect), nameof(RedirectAnalysis), nameof(RedirectDirection), - nameof(RedirectOperation), nameof(RedirectSource), - nameof(RedirectSourceKind), - nameof(ShellAnalysisLimits), nameof(ShellBlockSyntax), nameof(ShellGroupKind), nameof(ShellParserOptions), nameof(ShellSourceFragment), - nameof(ShellSyntaxKind), nameof(ShellSyntaxNode), nameof(ShellValueDomain), - nameof(ShellValueDomainKind), nameof(SimpleCommandSyntax), + nameof(HereDocumentRedirectAnalysis), + nameof(HereStringRedirectAnalysis), + nameof(UnresolvedRedirectAnalysis), nameof(VerbChain), }.OrderBy(n => n).ToArray(); diff --git a/tests/ShellSyntaxTree.Tests/ShellSyntaxProjectionTests.cs b/tests/ShellSyntaxTree.Tests/ShellSyntaxProjectionTests.cs index 7cfdb25..1dbee3d 100644 --- a/tests/ShellSyntaxTree.Tests/ShellSyntaxProjectionTests.cs +++ b/tests/ShellSyntaxTree.Tests/ShellSyntaxProjectionTests.cs @@ -19,6 +19,7 @@ public void Root_command_preserves_identity_and_joins_analysis_facts() { var clause = ClauseFor("echo") with { + Args = new[] { new Arg { Raw = "ready", Kind = ArgKind.Literal } }, Elements = new[] { new ClauseElement { Role = ClauseElementRole.Verb }, @@ -40,10 +41,10 @@ public void Root_command_preserves_identity_and_joins_analysis_facts() }; var effectiveArguments = new[] { - new EffectiveArgument + new EffectiveArgumentFacts { ClauseElementIndex = 1, - Value = new ShellValueDomain + Value = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { "ready" }, @@ -52,12 +53,12 @@ public void Root_command_preserves_identity_and_joins_analysis_facts() }; var redirects = new[] { - new RedirectAnalysis + new RedirectAnalysisFacts { RedirectIndex = 0, - Source = new RedirectSource { Kind = RedirectSourceKind.Default }, + Source = new RedirectSourceFacts { Kind = RedirectSourceKind.Default }, Operation = RedirectOperation.FileOutput, - Target = new ShellValueDomain + Target = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { "/work/out.txt" }, @@ -66,7 +67,7 @@ public void Root_command_preserves_identity_and_joins_analysis_facts() IsComplete = true, }, }; - var workingDirectory = new ShellValueDomain + var workingDirectory = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { "/work" }, @@ -89,11 +90,19 @@ public void Root_command_preserves_identity_and_joins_analysis_facts() Assert.Same(clause, Assert.Single(result.Clauses)); Assert.Equal(CommandOccurrenceRole.Ordinary, occurrence.ImmediateRole); Assert.True(occurrence.IsComplete); - Assert.Same(workingDirectory, occurrence.WorkingDirectory); - Assert.NotSame(effectiveArguments, occurrence.EffectiveArguments); - Assert.Same(effectiveArguments[0], Assert.Single(occurrence.EffectiveArguments)); - Assert.NotSame(redirects, occurrence.Redirects); - Assert.Same(redirects[0], Assert.Single(occurrence.Redirects)); + Assert.Equal("/work", Assert.IsType( + occurrence.WorkingDirectory).Value); + var argument = Assert.Single(occurrence.Arguments); + Assert.Same(clause.Args[0], argument.Argument); + Assert.Same(clause.Elements[1], argument.Element); + Assert.Equal("ready", Assert.IsType(argument.Value).Value); + var redirect = Assert.IsType( + Assert.Single(occurrence.Redirects)); + Assert.Same(clause.Redirects[0], redirect.Authored); + Assert.IsType(redirect.Source); + Assert.Equal(FileRedirectMode.Output, redirect.Mode); + Assert.Equal("/work/out.txt", Assert.IsType( + redirect.Target).Value); var rootFrame = Assert.Single(occurrence.Ancestry); Assert.Equal(ShellSyntaxKind.Block, rootFrame.AncestorKind); @@ -234,6 +243,11 @@ public void Command_owned_regions_follow_the_host_in_authored_order() { var hostClause = ClauseFor("ForEach-Object") with { + Args = new[] + { + new Arg { Raw = "end", Kind = ArgKind.DynamicSkip }, + new Arg { Raw = "begin", Kind = ArgKind.DynamicSkip }, + }, Elements = new[] { new ClauseElement { Role = ClauseElementRole.Verb }, @@ -262,13 +276,19 @@ public void Command_owned_regions_follow_the_host_in_authored_order() hostClauseElementIndex: 1, 2, Leaf("end", 3), - ExecutionRegionPhase.End), + ExecutionRegionPhase.End) with + { + HostArgument = hostClause.Elements[1], + }, ExecutionRegion( ExecutionRegionOrigin.CommandArgument, hostClauseElementIndex: 2, 4, Leaf("begin", 5), - ExecutionRegionPhase.Begin), + ExecutionRegionPhase.Begin) with + { + HostArgument = hostClause.Elements[2], + }, }, }; var pipeline = new PipelineSyntax @@ -313,6 +333,7 @@ public void Unknown_execution_region_facts_make_host_and_body_incomplete() { var clause = ClauseFor("Invoke-Custom") with { + Args = new[] { new Arg { Raw = "body", Kind = ArgKind.DynamicSkip } }, Elements = new[] { new ClauseElement { Role = ClauseElementRole.Verb }, @@ -330,6 +351,7 @@ public void Unknown_execution_region_facts_make_host_and_body_incomplete() new ExecutionRegionSyntax { Origin = ExecutionRegionOrigin.CommandArgument, + HostArgument = clause.Elements[1], HostClauseElementIndex = 1, Body = Block(2, Leaf("Remove-Item", 3)), }, @@ -449,15 +471,10 @@ public void Iterator_substitutions_keep_nearest_role_and_authored_indices() [Fact] public void Invalid_or_reused_substitution_children_discard_partial_projections() { - var nullCollection = Leaf("null-collection", 0) with + Assert.Throws(() => Leaf("null-collection", 0) with { Substitutions = null!, - }; - Assert.False(ShellSyntaxProjection.TryProject( - Block(0, nullCollection), - out var nullCollectionResult)); - Assert.Empty(nullCollectionResult.Commands); - Assert.Empty(nullCollectionResult.Clauses); + }); var nullChild = Leaf("null-child", 0) with { @@ -488,11 +505,6 @@ public void Nested_projection_is_source_ordered_and_uses_nearest_execution_role( var iteratorSort = Leaf("sort-iterator", 20); var print = Leaf("printf", 40); var bodySort = Leaf("sort-body", 50); - var test = Leaf("test", 70); - var remove = Leaf("rm", 80); - var probe = Leaf("probe", 90); - var echo = Leaf("echo", 100); - var fallback = Leaf("fallback", 110); var inner = Leaf("inner", 130); var loop = new ForEachSyntax @@ -518,29 +530,6 @@ public void Nested_projection_is_source_ordered_and_uses_nearest_execution_role( Stages = new ShellSyntaxNode[] { print, bodySort }, }), }; - var conditional = new ConditionalSyntax - { - SourceStart = 65, - SourceLength = 55, - Branches = new[] - { - new ConditionalBranchSyntax - { - SourceStart = 65, - SourceLength = 20, - Condition = Block(68, test), - Body = Block(78, remove), - }, - new ConditionalBranchSyntax - { - SourceStart = 86, - SourceLength = 20, - Condition = Block(88, probe), - Body = Block(98, echo), - }, - }, - Else = Block(108, fallback), - }; var substitution = new CommandSubstitutionSyntax { SourceStart = 125, @@ -551,7 +540,7 @@ public void Nested_projection_is_source_ordered_and_uses_nearest_execution_role( { SourceStart = 0, SourceLength = 140, - Statements = new ShellSyntaxNode[] { loop, conditional, substitution }, + Statements = new ShellSyntaxNode[] { loop, substitution }, }; var succeeded = ShellSyntaxProjection.TryProject(root, out var result); @@ -560,8 +549,7 @@ public void Nested_projection_is_source_ordered_and_uses_nearest_execution_role( Assert.Equal( new[] { - "find", "sort-iterator", "printf", "sort-body", "test", - "rm", "probe", "echo", "fallback", "inner", + "find", "sort-iterator", "printf", "sort-body", "inner", }, result.Commands.Select(Verb)); Assert.Equal( @@ -571,11 +559,6 @@ public void Nested_projection_is_source_ordered_and_uses_nearest_execution_role( CommandOccurrenceRole.PipelineStage, CommandOccurrenceRole.PipelineStage, CommandOccurrenceRole.PipelineStage, - CommandOccurrenceRole.Condition, - CommandOccurrenceRole.Branch, - CommandOccurrenceRole.Condition, - CommandOccurrenceRole.Branch, - CommandOccurrenceRole.Branch, CommandOccurrenceRole.Substitution, }, result.Commands.Select(command => command.ImmediateRole)); @@ -590,54 +573,10 @@ public void Nested_projection_is_source_ordered_and_uses_nearest_execution_role( AssertFrames( result.Commands[4], (ShellSyntaxKind.Block, CommandAncestryRegion.Root, 1), - (ShellSyntaxKind.Conditional, CommandAncestryRegion.Branch, 0), - (ShellSyntaxKind.ConditionalBranch, CommandAncestryRegion.Condition, null), - (ShellSyntaxKind.Block, CommandAncestryRegion.Statement, 0)); - AssertFrames( - result.Commands[8], - (ShellSyntaxKind.Block, CommandAncestryRegion.Root, 1), - (ShellSyntaxKind.Conditional, CommandAncestryRegion.Branch, 2), - (ShellSyntaxKind.Block, CommandAncestryRegion.Statement, 0)); - AssertFrames( - result.Commands[9], - (ShellSyntaxKind.Block, CommandAncestryRegion.Root, 2), - (ShellSyntaxKind.CommandSubstitution, CommandAncestryRegion.Substitution, 2), + (ShellSyntaxKind.CommandSubstitution, CommandAncestryRegion.Substitution, 1), (ShellSyntaxKind.Block, CommandAncestryRegion.Statement, 0)); } - [Fact] - public void Compatibility_projection_preserves_authored_operators_without_synthesizing_relations() - { - var condition = ClauseFor("test"); - var thenClause = ClauseFor("rm", CompoundOperator.AndIf); - var elseClause = ClauseFor("echo"); - var root = Block( - 0, - new ConditionalSyntax - { - Branches = new[] - { - new ConditionalBranchSyntax - { - Condition = Block(1, Leaf(condition, 1)), - Body = Block(2, Leaf(thenClause, 2)), - }, - }, - Else = Block(3, Leaf(elseClause, 3)), - }); - - var succeeded = ShellSyntaxProjection.TryProject(root, out var result); - - Assert.True(succeeded); - Assert.Equal(3, result.Clauses.Count); - Assert.Same(condition, result.Clauses[0]); - Assert.Same(thenClause, result.Clauses[1]); - Assert.Same(elseClause, result.Clauses[2]); - Assert.Equal( - new[] { CompoundOperator.None, CompoundOperator.AndIf, CompoundOperator.None }, - result.Clauses.Select(clause => clause.Operator)); - } - [Fact] public void Sixteen_structural_containers_are_supported() { @@ -710,7 +649,7 @@ public void Structural_depth_overflow_discards_partial_projections() } [Fact] - public void Cyclic_syntax_discards_partial_projections() + public void Published_collections_cannot_be_mutated_into_cycles() { var statements = new List(); var root = new ShellBlockSyntax { Statements = statements }; @@ -719,7 +658,7 @@ public void Cyclic_syntax_discards_partial_projections() var succeeded = ShellSyntaxProjection.TryProject(root, out var result); - Assert.False(succeeded); + Assert.True(succeeded); Assert.Empty(result.Commands); Assert.Empty(result.Clauses); } @@ -827,6 +766,7 @@ public void Invalid_command_owned_execution_regions_fail_closed() { var clause = ClauseFor("host") with { + Args = new[] { new Arg { Raw = "body", Kind = ArgKind.DynamicSkip } }, Elements = new[] { new ClauseElement { Role = ClauseElementRole.Verb }, @@ -841,10 +781,12 @@ public void Invalid_command_owned_execution_regions_fail_closed() ExecutionRegionOrigin.CommandArgument, hostClauseElementIndex: 1, 2, - Leaf("body", 3)); + Leaf("body", 3)) with + { + HostArgument = clause.Elements[1], + }; var invalidCollections = new IReadOnlyList[] { - null!, new ExecutionRegionSyntax[] { null! }, new[] { valid with { Origin = ExecutionRegionOrigin.DirectCall } }, new[] { valid with { HostClauseElementIndex = null } }, @@ -863,6 +805,9 @@ valid with }, }; + Assert.Throws(() => + Leaf(clause, 1) with { ExecutionRegions = null! }); + foreach (var executionRegions in invalidCollections) { var host = Leaf(clause, 1) with { ExecutionRegions = executionRegions }; @@ -898,22 +843,14 @@ clause with } [Fact] - public void Group_and_command_list_retain_condition_and_body_roles() + public void Group_and_command_list_preserve_ordinary_role_and_ancestry() { - var condition = Leaf("condition", 4); var body = Leaf("body", 12); var root = Block( 0, - new ConditionLoopSyntax + new GroupSyntax { - LoopKind = ConditionLoopKind.While, - Condition = Block( - 2, - new GroupSyntax - { - GroupKind = ShellGroupKind.CurrentScope, - Body = Block(3, condition), - }), + GroupKind = ShellGroupKind.CurrentScope, Body = Block( 10, new CommandListSyntax @@ -932,19 +869,11 @@ public void Group_and_command_list_retain_condition_and_body_roles() var succeeded = ShellSyntaxProjection.TryProject(root, out var result); Assert.True(succeeded); - Assert.Equal(CommandOccurrenceRole.Condition, result.Commands[0].ImmediateRole); - Assert.Equal(CommandOccurrenceRole.LoopBody, result.Commands[1].ImmediateRole); + Assert.Equal(CommandOccurrenceRole.Ordinary, result.Commands[0].ImmediateRole); AssertFrames( result.Commands[0], (ShellSyntaxKind.Block, CommandAncestryRegion.Root, 0), - (ShellSyntaxKind.ConditionLoop, CommandAncestryRegion.Condition, null), - (ShellSyntaxKind.Block, CommandAncestryRegion.Statement, 0), (ShellSyntaxKind.Group, CommandAncestryRegion.GroupBody, null), - (ShellSyntaxKind.Block, CommandAncestryRegion.Statement, 0)); - AssertFrames( - result.Commands[1], - (ShellSyntaxKind.Block, CommandAncestryRegion.Root, 0), - (ShellSyntaxKind.ConditionLoop, CommandAncestryRegion.LoopBody, null), (ShellSyntaxKind.Block, CommandAncestryRegion.Statement, 0), (ShellSyntaxKind.CommandList, CommandAncestryRegion.Statement, 0)); } @@ -977,6 +906,7 @@ public void Invalid_value_domains_and_argument_coordinates_fail_closed() { var clause = ClauseFor("echo") with { + Args = new[] { new Arg { Raw = "value", Kind = ArgKind.Literal } }, Elements = new[] { new ClauseElement { Role = ClauseElementRole.Verb }, @@ -988,7 +918,7 @@ public void Invalid_value_domains_and_argument_coordinates_fail_closed() { () => new CommandOccurrenceFacts { - WorkingDirectory = new ShellValueDomain + WorkingDirectory = new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = new[] { "/a", "/b" }, @@ -998,10 +928,10 @@ public void Invalid_value_domains_and_argument_coordinates_fail_closed() { EffectiveArguments = new[] { - new EffectiveArgument + new EffectiveArgumentFacts { ClauseElementIndex = 0, - Value = ShellValueDomain.Unknown, + Value = ShellValueDomainFacts.Unknown, }, }, }, @@ -1009,10 +939,10 @@ public void Invalid_value_domains_and_argument_coordinates_fail_closed() { EffectiveArguments = new[] { - new EffectiveArgument + new EffectiveArgumentFacts { ClauseElementIndex = 1, - Value = new ShellValueDomain { Kind = ShellValueDomainKind.Exact }, + Value = new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact }, }, }, }, @@ -1020,15 +950,15 @@ public void Invalid_value_domains_and_argument_coordinates_fail_closed() { EffectiveArguments = new[] { - new EffectiveArgument + new EffectiveArgumentFacts { ClauseElementIndex = 1, - Value = ShellValueDomain.Unknown, + Value = ShellValueDomainFacts.Unknown, }, - new EffectiveArgument + new EffectiveArgumentFacts { ClauseElementIndex = 1, - Value = ShellValueDomain.Unknown, + Value = ShellValueDomainFacts.Unknown, }, }, }, @@ -1051,6 +981,7 @@ public void Value_domain_validator_accepts_only_the_four_locked_shapes() { var clause = ClauseFor("echo") with { + Args = new[] { new Arg { Raw = "value", Kind = ArgKind.Literal } }, Elements = new[] { new ClauseElement { Role = ClauseElementRole.Verb }, @@ -1060,18 +991,18 @@ public void Value_domain_validator_accepts_only_the_four_locked_shapes() var root = Block(0, Leaf(clause, 0)); var validDomains = new[] { - ShellValueDomain.Unknown, - new ShellValueDomain + ShellValueDomainFacts.Unknown, + new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { "one" }, }, - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = new[] { "one", "two" }, }, - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.Pattern, Pattern = "/work/*.txt", @@ -1096,38 +1027,38 @@ public void Value_domain_validator_accepts_only_the_four_locked_shapes() .ToArray(); var invalidDomains = new[] { - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.Unknown, Values = new[] { "unexpected" }, }, - new ShellValueDomain { Kind = ShellValueDomainKind.Exact }, - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact }, + new ShellValueDomainFacts { Kind = ShellValueDomainKind.Exact, Values = new[] { "one", "two" }, }, - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = new[] { "one" }, }, - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = new[] { "duplicate", "duplicate" }, }, - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.FiniteSet, Values = overLimit, }, - new ShellValueDomain + new ShellValueDomainFacts { Kind = ShellValueDomainKind.Pattern, Pattern = "/work/*.txt", }, - new ShellValueDomain { Kind = (ShellValueDomainKind)999 }, + new ShellValueDomainFacts { Kind = (ShellValueDomainKind)999 }, }; foreach (var domain in invalidDomains) @@ -1152,24 +1083,24 @@ public void Invalid_redirect_facts_fail_closed() var root = Block(0, Leaf(clause, 0)); var invalidRedirects = new[] { - new RedirectAnalysis + new RedirectAnalysisFacts { RedirectIndex = 1, - Source = new RedirectSource { Kind = RedirectSourceKind.Default }, + Source = new RedirectSourceFacts { Kind = RedirectSourceKind.Default }, Operation = RedirectOperation.FileOutput, IsPathRelevant = true, }, - new RedirectAnalysis + new RedirectAnalysisFacts { RedirectIndex = 0, Operation = RedirectOperation.FileOutput, IsPathRelevant = true, IsComplete = true, }, - new RedirectAnalysis + new RedirectAnalysisFacts { RedirectIndex = 0, - Source = new RedirectSource + Source = new RedirectSourceFacts { Kind = RedirectSourceKind.Default, Descriptor = 1, @@ -1177,16 +1108,16 @@ public void Invalid_redirect_facts_fail_closed() Operation = RedirectOperation.FileOutput, IsPathRelevant = true, }, - new RedirectAnalysis + new RedirectAnalysisFacts { RedirectIndex = 0, - Source = new RedirectSource { Kind = RedirectSourceKind.Default }, + Source = new RedirectSourceFacts { Kind = RedirectSourceKind.Default }, Operation = RedirectOperation.HereDocument, }, - new RedirectAnalysis + new RedirectAnalysisFacts { RedirectIndex = 0, - Source = new RedirectSource { Kind = RedirectSourceKind.Default }, + Source = new RedirectSourceFacts { Kind = RedirectSourceKind.Default }, Operation = RedirectOperation.HereDocument, HereDocument = new HereDocumentAnalysis { @@ -1215,6 +1146,45 @@ public void Invalid_redirect_facts_fail_closed() } } + [Theory] + [InlineData(false, null)] + [InlineData(true, 2)] + public void Known_redirect_sources_cannot_publish_an_unresolved_operation( + bool isDescriptor, + int? descriptor) + { + var clause = ClauseFor("echo") with + { + Redirects = new[] { new Redirect() }, + }; + var root = Block(0, Leaf(clause, 0)); + + var succeeded = ShellSyntaxProjection.TryProject( + root, + _ => new CommandOccurrenceFacts + { + Redirects = new[] + { + new RedirectAnalysisFacts + { + RedirectIndex = 0, + Source = new RedirectSourceFacts + { + Kind = isDescriptor + ? RedirectSourceKind.Descriptor + : RedirectSourceKind.Default, + Descriptor = descriptor, + }, + }, + }, + }, + out var result); + + Assert.False(succeeded); + Assert.Empty(result.Commands); + Assert.Empty(result.Clauses); + } + [Fact] public void Invalid_parser_owned_analysis_discards_partial_projections() { @@ -1306,6 +1276,7 @@ private static ShellBlockSyntax DecodedExecutionRegionTree() Leaf("body", 3), ExecutionRegionPhase.End) with { + HostArgument = hostClause.Elements[1], Timing = ExecutionRegionTiming.Concurrent, Cardinality = ExecutionRegionCardinality.ZeroOrMore, }, @@ -1458,12 +1429,12 @@ private static LoopBindingSyntax Binding(string name) => Source = new ShellSourceFragment { Raw = name }, }; - private static CommandOccurrenceFacts FactsForArgument(ShellValueDomain domain) => + private static CommandOccurrenceFacts FactsForArgument(ShellValueDomainFacts domain) => new() { EffectiveArguments = new[] { - new EffectiveArgument + new EffectiveArgumentFacts { ClauseElementIndex = 1, Value = domain, diff --git a/tests/ShellSyntaxTree.Tests/V03PublicApiSnapshotTests.cs b/tests/ShellSyntaxTree.Tests/V03PublicApiSnapshotTests.cs index 6454e65..ed81b95 100644 --- a/tests/ShellSyntaxTree.Tests/V03PublicApiSnapshotTests.cs +++ b/tests/ShellSyntaxTree.Tests/V03PublicApiSnapshotTests.cs @@ -12,675 +12,421 @@ namespace ShellSyntaxTree.Tests; -/// Locks the additive v0.3 public API and its safe defaults. +/// Locks the stable-v0.3 parser-owned public result surface. public class V03PublicApiSnapshotTests { [Fact] - public void Syntax_node_base_is_record_shaped_and_closed_to_external_implementations() + public void Alpha_only_public_shapes_are_absent() { - var type = typeof(ShellSyntaxNode); - Assert.True(type.IsPublic); - Assert.True(type.IsAbstract); - AssertRecord(type); - - AssertGetProperty(type, nameof(ShellSyntaxNode.Kind), typeof(ShellSyntaxKind)); - AssertInitProperty(type, nameof(ShellSyntaxNode.SourceStart), typeof(int?)); - AssertInitProperty(type, nameof(ShellSyntaxNode.SourceLength), typeof(int?)); - Assert.Equal( - new[] - { - nameof(ShellSyntaxNode.Kind), - nameof(ShellSyntaxNode.SourceLength), - nameof(ShellSyntaxNode.SourceStart), - }, - DeclaredPublicProperties(type)); - - var constructor = type.GetConstructor( - BindingFlags.Instance | BindingFlags.NonPublic, - binder: null, - Type.EmptyTypes, - modifiers: null); - Assert.NotNull(constructor); - Assert.True(constructor!.IsFamilyAndAssembly); + var assembly = typeof(ShellSyntaxNode).Assembly; + var removed = new[] + { + "ShellSyntaxTree.ShellSyntaxKind", + "ShellSyntaxTree.LoopBindingSyntax", + "ShellSyntaxTree.ConditionLoopSyntax", + "ShellSyntaxTree.ConditionLoopKind", + "ShellSyntaxTree.ConditionalSyntax", + "ShellSyntaxTree.ConditionalBranchSyntax", + "ShellSyntaxTree.EffectiveArgument", + "ShellSyntaxTree.ShellValueDomainKind", + "ShellSyntaxTree.RedirectOperation", + "ShellSyntaxTree.RedirectSourceKind", + "ShellSyntaxTree.ShellAnalysisLimits", + }; - var ownership = type.GetProperty( - "IsLibraryOwnedNode", - BindingFlags.Instance | BindingFlags.NonPublic); - Assert.NotNull(ownership); - Assert.NotNull(ownership!.GetMethod); - Assert.True(ownership.GetMethod!.IsAbstract); - Assert.True(ownership.GetMethod.IsFamilyAndAssembly); + foreach (var name in removed) + { + var type = assembly.GetType(name, throwOnError: false); + Assert.True(type is null || (!type.IsPublic && !type.IsNestedPublic)); + } } [Fact] - public void Syntax_records_match_the_locked_members_and_defaults() + public void Syntax_family_matches_the_stable_contract() { - AssertNode( - new ShellBlockSyntax(), - ShellSyntaxKind.Block, + AssertClosedBase( + typeof(ShellSyntaxNode), + (nameof(ShellSyntaxNode.SourceStart), typeof(int?)), + (nameof(ShellSyntaxNode.SourceLength), typeof(int?))); + AssertResultRecord( + typeof(ShellBlockSyntax), (nameof(ShellBlockSyntax.Statements), typeof(IReadOnlyList))); - AssertNode( - new SimpleCommandSyntax(), - ShellSyntaxKind.SimpleCommand, + AssertResultRecord( + typeof(SimpleCommandSyntax), (nameof(SimpleCommandSyntax.Clause), typeof(Clause)), (nameof(SimpleCommandSyntax.Substitutions), typeof(IReadOnlyList)), (nameof(SimpleCommandSyntax.ExecutionRegions), typeof(IReadOnlyList))); - AssertNode( - new PipelineSyntax(), - ShellSyntaxKind.Pipeline, + AssertResultRecord( + typeof(PipelineSyntax), (nameof(PipelineSyntax.Stages), typeof(IReadOnlyList))); - AssertNode( - new CommandListSyntax(), - ShellSyntaxKind.CommandList, + AssertResultRecord( + typeof(CommandListSyntax), (nameof(CommandListSyntax.Items), typeof(IReadOnlyList))); - AssertNode( - new GroupSyntax(), - ShellSyntaxKind.Group, + AssertResultRecord( + typeof(CommandListItemSyntax), + (nameof(CommandListItemSyntax.Operator), typeof(CompoundOperator)), + (nameof(CommandListItemSyntax.Command), typeof(ShellSyntaxNode))); + AssertResultRecord( + typeof(GroupSyntax), (nameof(GroupSyntax.GroupKind), typeof(ShellGroupKind)), (nameof(GroupSyntax.Body), typeof(ShellBlockSyntax))); - AssertNode( - new ForEachSyntax(), - ShellSyntaxKind.ForEach, - (nameof(ForEachSyntax.Binding), typeof(LoopBindingSyntax)), + AssertResultRecord( + typeof(ForEachSyntax), + (nameof(ForEachSyntax.BindingName), typeof(string)), + (nameof(ForEachSyntax.BindingSource), typeof(ShellSourceFragment)), (nameof(ForEachSyntax.Iterable), typeof(ShellSourceFragment)), (nameof(ForEachSyntax.IteratorCommands), typeof(ShellBlockSyntax)), (nameof(ForEachSyntax.Body), typeof(ShellBlockSyntax))); - AssertNode( - new ConditionLoopSyntax(), - ShellSyntaxKind.ConditionLoop, - (nameof(ConditionLoopSyntax.LoopKind), typeof(ConditionLoopKind)), - (nameof(ConditionLoopSyntax.Condition), typeof(ShellBlockSyntax)), - (nameof(ConditionLoopSyntax.Body), typeof(ShellBlockSyntax))); - AssertNode( - new ConditionalSyntax(), - ShellSyntaxKind.Conditional, - (nameof(ConditionalSyntax.Branches), typeof(IReadOnlyList)), - (nameof(ConditionalSyntax.Else), typeof(ShellBlockSyntax))); - AssertNode( - new ConditionalBranchSyntax(), - ShellSyntaxKind.ConditionalBranch, - (nameof(ConditionalBranchSyntax.Condition), typeof(ShellBlockSyntax)), - (nameof(ConditionalBranchSyntax.Body), typeof(ShellBlockSyntax))); - AssertNode( - new CommandSubstitutionSyntax(), - ShellSyntaxKind.CommandSubstitution, + AssertResultRecord( + typeof(ShellSourceFragment), + (nameof(ShellSourceFragment.Raw), typeof(string)), + (nameof(ShellSourceFragment.SourceStart), typeof(int?)), + (nameof(ShellSourceFragment.SourceLength), typeof(int?))); + AssertResultRecord( + typeof(CommandSubstitutionSyntax), (nameof(CommandSubstitutionSyntax.Body), typeof(ShellBlockSyntax))); - AssertNode( - new ExecutionRegionSyntax(), - ShellSyntaxKind.ExecutionRegion, + AssertResultRecord( + typeof(ExecutionRegionSyntax), (nameof(ExecutionRegionSyntax.Origin), typeof(ExecutionRegionOrigin)), - (nameof(ExecutionRegionSyntax.HostClauseElementIndex), typeof(int?)), + (nameof(ExecutionRegionSyntax.HostArgument), typeof(ClauseElement)), (nameof(ExecutionRegionSyntax.Phase), typeof(ExecutionRegionPhase)), (nameof(ExecutionRegionSyntax.Timing), typeof(ExecutionRegionTiming)), (nameof(ExecutionRegionSyntax.Cardinality), typeof(ExecutionRegionCardinality)), (nameof(ExecutionRegionSyntax.Body), typeof(ShellBlockSyntax))); - - var simple = new SimpleCommandSyntax(); - Assert.NotNull(simple.Clause); - Assert.NotNull(simple.Substitutions); - Assert.Empty(simple.Substitutions); - Assert.NotNull(simple.ExecutionRegions); - Assert.Empty(simple.ExecutionRegions); - Assert.Empty(new ShellBlockSyntax().Statements); - Assert.Empty(new PipelineSyntax().Stages); - Assert.Empty(new CommandListSyntax().Items); - var group = new GroupSyntax(); - Assert.Equal(ShellGroupKind.Unknown, group.GroupKind); - Assert.NotNull(group.Body); - Assert.Empty(group.Body.Statements); - - var forEach = new ForEachSyntax(); - Assert.NotNull(forEach.Binding); - Assert.NotNull(forEach.Iterable); - Assert.NotNull(forEach.IteratorCommands); - Assert.NotNull(forEach.Body); - Assert.Empty(forEach.IteratorCommands.Statements); - Assert.Empty(forEach.Body.Statements); - - var conditionLoop = new ConditionLoopSyntax(); - Assert.Equal(ConditionLoopKind.Unknown, conditionLoop.LoopKind); - Assert.NotNull(conditionLoop.Condition); - Assert.NotNull(conditionLoop.Body); - Assert.Empty(conditionLoop.Condition.Statements); - Assert.Empty(conditionLoop.Body.Statements); - - var conditional = new ConditionalSyntax(); - Assert.Empty(conditional.Branches); - Assert.Null(conditional.Else); - - var branch = new ConditionalBranchSyntax(); - Assert.NotNull(branch.Condition); - Assert.NotNull(branch.Body); - Assert.Empty(branch.Condition.Statements); - Assert.Empty(branch.Body.Statements); - - var substitution = new CommandSubstitutionSyntax(); - Assert.NotNull(substitution.Body); - Assert.Empty(substitution.Body.Statements); - - var executionRegion = new ExecutionRegionSyntax(); - Assert.Equal(ExecutionRegionOrigin.Unknown, executionRegion.Origin); - Assert.Null(executionRegion.HostClauseElementIndex); - Assert.Equal(ExecutionRegionPhase.Unknown, executionRegion.Phase); - Assert.Equal(ExecutionRegionTiming.Unknown, executionRegion.Timing); - Assert.Equal(ExecutionRegionCardinality.Unknown, executionRegion.Cardinality); - Assert.NotNull(executionRegion.Body); - Assert.Empty(executionRegion.Body.Statements); } [Fact] - public void Supporting_syntax_records_match_the_locked_members_and_defaults() + public void Occurrence_family_matches_the_stable_contract() { - AssertRecordWithInitProperties( - typeof(CommandListItemSyntax), - (nameof(CommandListItemSyntax.Operator), typeof(CompoundOperator)), - (nameof(CommandListItemSyntax.Command), typeof(ShellSyntaxNode))); - var item = new CommandListItemSyntax(); - Assert.Equal(CompoundOperator.None, item.Operator); - Assert.IsType(item.Command); - - AssertRecordWithInitProperties( - typeof(LoopBindingSyntax), - (nameof(LoopBindingSyntax.Name), typeof(string)), - (nameof(LoopBindingSyntax.Source), typeof(ShellSourceFragment))); - var binding = new LoopBindingSyntax(); - Assert.Equal("", binding.Name); - Assert.NotNull(binding.Source); - - AssertRecordWithInitProperties( - typeof(ShellSourceFragment), - (nameof(ShellSourceFragment.Raw), typeof(string)), - (nameof(ShellSourceFragment.SourceStart), typeof(int?)), - (nameof(ShellSourceFragment.SourceLength), typeof(int?))); - var fragment = new ShellSourceFragment(); - Assert.Equal("", fragment.Raw); - Assert.Null(fragment.SourceStart); - Assert.Null(fragment.SourceLength); - } - - [Fact] - public void Command_occurrence_records_match_the_locked_members_and_defaults() - { - AssertRecordWithInitProperties( + AssertResultRecord( typeof(CommandOccurrence), (nameof(CommandOccurrence.Clause), typeof(Clause)), (nameof(CommandOccurrence.ImmediateRole), typeof(CommandOccurrenceRole)), - (nameof(CommandOccurrence.Ancestry), typeof(IReadOnlyList)), - (nameof(CommandOccurrence.EffectiveArguments), typeof(IReadOnlyList)), + (nameof(CommandOccurrence.Ancestry), + typeof(IReadOnlyList)), + (nameof(CommandOccurrence.Arguments), typeof(IReadOnlyList)), (nameof(CommandOccurrence.WorkingDirectory), typeof(ShellValueDomain)), (nameof(CommandOccurrence.Redirects), typeof(IReadOnlyList)), (nameof(CommandOccurrence.IsComplete), typeof(bool))); - var occurrence = new CommandOccurrence(); - Assert.NotNull(occurrence.Clause); - Assert.Equal(CommandOccurrenceRole.Unknown, occurrence.ImmediateRole); - Assert.Empty(occurrence.Ancestry); - Assert.Empty(occurrence.EffectiveArguments); - Assert.Same(ShellValueDomain.Unknown, occurrence.WorkingDirectory); - Assert.Empty(occurrence.Redirects); - Assert.False(occurrence.IsComplete); - - AssertRecordWithInitProperties( + AssertResultRecord( typeof(CommandAncestryFrame), - (nameof(CommandAncestryFrame.AncestorKind), typeof(ShellSyntaxKind)), + (nameof(CommandAncestryFrame.Ancestor), typeof(ShellSyntaxNode)), (nameof(CommandAncestryFrame.Region), typeof(CommandAncestryRegion)), - (nameof(CommandAncestryFrame.ChildIndex), typeof(int?)), - (nameof(CommandAncestryFrame.SourceStart), typeof(int?)), - (nameof(CommandAncestryFrame.SourceLength), typeof(int?))); - var ancestry = new CommandAncestryFrame(); - Assert.Equal(ShellSyntaxKind.Unknown, ancestry.AncestorKind); - Assert.Equal(CommandAncestryRegion.Unknown, ancestry.Region); - Assert.Null(ancestry.ChildIndex); - Assert.Null(ancestry.SourceStart); - Assert.Null(ancestry.SourceLength); - - AssertRecordWithInitProperties( - typeof(EffectiveArgument), - (nameof(EffectiveArgument.ClauseElementIndex), typeof(int)), - (nameof(EffectiveArgument.Value), typeof(ShellValueDomain))); - var argument = new EffectiveArgument(); - Assert.Equal(-1, argument.ClauseElementIndex); - Assert.Same(ShellValueDomain.Unknown, argument.Value); + (nameof(CommandAncestryFrame.ChildIndex), typeof(int?))); + AssertResultRecord( + typeof(AnalyzedArgument), + (nameof(AnalyzedArgument.Argument), typeof(Arg)), + (nameof(AnalyzedArgument.Element), typeof(ClauseElement)), + (nameof(AnalyzedArgument.Value), typeof(ShellValueDomain))); + + AssertEnum( + "Unknown", "Ordinary", "PipelineStage", "Iterator", "LoopBody", + "Substitution", "ExecutionRegion"); + AssertEnum( + "Unknown", "Root", "Statement", "PipelineStage", "GroupBody", + "Iterator", "LoopBody", "Substitution", "ExecutionRegion"); } [Fact] - public void Shell_value_domain_matches_the_locked_members_and_defaults() + public void Value_domain_is_a_closed_runtime_discriminated_family() { - AssertRecordWithInitProperties( - typeof(ShellValueDomain), - (nameof(ShellValueDomain.Kind), typeof(ShellValueDomainKind)), - (nameof(ShellValueDomain.Values), typeof(IReadOnlyList)), - (nameof(ShellValueDomain.Pattern), typeof(string)), - (nameof(ShellValueDomain.CoveringDirectory), typeof(string))); - - var unknown = ShellValueDomain.Unknown; - Assert.Same(unknown, ShellValueDomain.Unknown); - Assert.Equal(ShellValueDomainKind.Unknown, unknown.Kind); - Assert.Empty(unknown.Values); - Assert.Null(unknown.Pattern); - Assert.Null(unknown.CoveringDirectory); - - var property = typeof(ShellValueDomain).GetProperty( - nameof(ShellValueDomain.Unknown), - BindingFlags.Public | BindingFlags.Static); - Assert.NotNull(property); - Assert.True(property!.CanRead); - Assert.False(property.CanWrite); - Assert.Equal( - new[] { nameof(ShellValueDomain.Unknown) }, - typeof(ShellValueDomain) - .GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) - .Select(candidate => candidate.Name) - .OrderBy(name => name)); + AssertClosedBase(typeof(ShellValueDomain)); + AssertResultRecord(typeof(ShellValueDomain.Unknown)); + AssertResultRecordAccessors( + typeof(ShellValueDomain.Exact), + (nameof(ShellValueDomain.Exact.Value), typeof(string), false)); + AssertResultRecordAccessors( + typeof(ShellValueDomain.FiniteSet), + (nameof(ShellValueDomain.FiniteSet.Values), + typeof(IReadOnlyList), false)); + AssertResultRecordAccessors( + typeof(ShellValueDomain.PathPattern), + (nameof(ShellValueDomain.PathPattern.Pattern), typeof(string), false), + (nameof(ShellValueDomain.PathPattern.CoveringDirectory), typeof(string), false)); } [Fact] - public void Redirect_analysis_records_match_the_locked_members_and_defaults() + public void Redirect_families_match_the_stable_contract() { - AssertRecordWithInitProperties( + AssertClosedBase(typeof(RedirectSource)); + AssertResultRecord(typeof(RedirectSource.Unknown)); + AssertResultRecord(typeof(RedirectSource.Default)); + AssertResultRecordAccessors( + typeof(RedirectSource.Descriptor), + (nameof(RedirectSource.Descriptor.Value), typeof(int), false)); + AssertResultRecord(typeof(RedirectSource.PowerShellAllStreams)); + + AssertClosedBase( typeof(RedirectAnalysis), - (nameof(RedirectAnalysis.RedirectIndex), typeof(int)), + (nameof(RedirectAnalysis.Authored), typeof(Redirect)), (nameof(RedirectAnalysis.Source), typeof(RedirectSource)), - (nameof(RedirectAnalysis.Operation), typeof(RedirectOperation)), - (nameof(RedirectAnalysis.TargetDescriptor), typeof(int?)), - (nameof(RedirectAnalysis.Target), typeof(ShellValueDomain)), - (nameof(RedirectAnalysis.HereDocument), typeof(HereDocumentAnalysis)), - (nameof(RedirectAnalysis.IsPathRelevant), typeof(bool)), (nameof(RedirectAnalysis.IsComplete), typeof(bool))); - var redirect = new RedirectAnalysis(); - Assert.Equal(-1, redirect.RedirectIndex); - Assert.NotNull(redirect.Source); - Assert.Equal(RedirectOperation.Unknown, redirect.Operation); - Assert.Null(redirect.TargetDescriptor); - Assert.Same(ShellValueDomain.Unknown, redirect.Target); - Assert.Null(redirect.HereDocument); - Assert.False(redirect.IsPathRelevant); - Assert.False(redirect.IsComplete); - - AssertRecordWithInitProperties( + AssertResultRecordAccessors( + typeof(FileRedirectAnalysis), + (nameof(FileRedirectAnalysis.Mode), typeof(FileRedirectMode), false), + (nameof(FileRedirectAnalysis.Target), typeof(ShellValueDomain), true)); + AssertResultRecord(typeof(UnresolvedRedirectAnalysis)); + AssertResultRecord( + typeof(DescriptorDuplicateRedirectAnalysis), + (nameof(DescriptorDuplicateRedirectAnalysis.TargetDescriptor), typeof(int))); + AssertResultRecord( + typeof(DescriptorMoveRedirectAnalysis), + (nameof(DescriptorMoveRedirectAnalysis.TargetDescriptor), typeof(int))); + AssertResultRecord(typeof(DescriptorCloseRedirectAnalysis)); + AssertResultRecord( + typeof(HereDocumentRedirectAnalysis), + (nameof(HereDocumentRedirectAnalysis.Document), + typeof(HereDocumentAnalysis))); + AssertResultRecord( + typeof(HereStringRedirectAnalysis), + (nameof(HereStringRedirectAnalysis.Data), typeof(ShellValueDomain))); + AssertResultRecord( typeof(HereDocumentAnalysis), (nameof(HereDocumentAnalysis.Delimiter), typeof(ShellSourceFragment)), (nameof(HereDocumentAnalysis.Body), typeof(ShellSourceFragment)), - (nameof(HereDocumentAnalysis.ExpansionMode), typeof(HereDocumentExpansionMode)), + (nameof(HereDocumentAnalysis.ExpansionMode), + typeof(HereDocumentExpansionMode)), (nameof(HereDocumentAnalysis.StripLeadingTabs), typeof(bool)), (nameof(HereDocumentAnalysis.IsComplete), typeof(bool))); - var hereDocument = new HereDocumentAnalysis(); - Assert.NotNull(hereDocument.Delimiter); - Assert.NotNull(hereDocument.Body); - Assert.Equal(HereDocumentExpansionMode.Unknown, hereDocument.ExpansionMode); - Assert.False(hereDocument.StripLeadingTabs); - Assert.False(hereDocument.IsComplete); - - AssertRecordWithInitProperties( - typeof(RedirectSource), - (nameof(RedirectSource.Kind), typeof(RedirectSourceKind)), - (nameof(RedirectSource.Descriptor), typeof(int?))); - var source = new RedirectSource(); - Assert.Equal(RedirectSourceKind.Unknown, source.Kind); - Assert.Null(source.Descriptor); - } - [Fact] - public void V03_enums_reserve_zero_for_unknown_and_match_the_locked_order() - { - AssertEnum( - "Unknown", "Block", "SimpleCommand", "Pipeline", "CommandList", - "Group", "ForEach", "ConditionLoop", "Conditional", - "ConditionalBranch", "CommandSubstitution", "ExecutionRegion"); - AssertEnum("Unknown", "CurrentScope", "IsolatedScope"); - AssertEnum("Unknown", "While", "Until"); - AssertEnum( - "Unknown", "DirectCall", "DotSource", "CommandArgument"); - AssertEnum( - "Unknown", "Main", "Initialization", "Begin", "Process", "End", - "Filter", "Action", "Completion"); - AssertEnum( - "Unknown", "Synchronous", "Concurrent", "Deferred"); - AssertEnum( - "Unknown", "Once", "OncePerInputObject", "ZeroOrMore"); - AssertEnum( - "Unknown", "Ordinary", "PipelineStage", "Condition", "Iterator", - "LoopBody", "Branch", "Substitution", "ExecutionRegion"); - AssertEnum( - "Unknown", "Root", "Statement", "PipelineStage", "GroupBody", - "Iterator", "LoopBody", "Condition", "Branch", "Substitution", - "ExecutionRegion"); - AssertEnum("Unknown", "Exact", "FiniteSet", "Pattern"); + AssertEnum( + "Input", "Output", "Append", "CombinedOutput", "CombinedOutputAppend"); AssertEnum("Unknown", "Literal", "Expand"); - AssertEnum( - "Unknown", "Default", "Descriptor", "PowerShellAllStreams"); - AssertEnum( - "Unknown", "FileInput", "FileOutput", "FileAppend", - "DescriptorDuplicate", "DescriptorClose", "DescriptorMove", - "CombinedOutput", "CombinedOutputAppend", "HereDocument", "HereString"); } [Fact] - public void V03_reference_property_nullability_matches_the_locked_contract() + public void Public_read_only_lists_are_defensive_copies() { - var types = new[] + var statement = new SimpleCommandSyntax(); + var statements = new ShellSyntaxNode[] { statement }; + var block = new ShellBlockSyntax { Statements = statements }; + statements[0] = new PipelineSyntax(); + Assert.Same(statement, Assert.Single(block.Statements)); + + var substitution = new CommandSubstitutionSyntax(); + var substitutions = new[] { substitution }; + var executionRegion = new ExecutionRegionSyntax(); + var executionRegions = new[] { executionRegion }; + var command = new SimpleCommandSyntax { - typeof(ParsedCommand), - typeof(ShellSyntaxNode), - typeof(ShellBlockSyntax), - typeof(SimpleCommandSyntax), - typeof(PipelineSyntax), - typeof(CommandListSyntax), - typeof(CommandListItemSyntax), - typeof(GroupSyntax), - typeof(ForEachSyntax), - typeof(LoopBindingSyntax), - typeof(ShellSourceFragment), - typeof(ConditionLoopSyntax), - typeof(ConditionalSyntax), - typeof(ConditionalBranchSyntax), - typeof(CommandSubstitutionSyntax), - typeof(ExecutionRegionSyntax), - typeof(CommandOccurrence), - typeof(CommandAncestryFrame), - typeof(EffectiveArgument), - typeof(ShellValueDomain), - typeof(RedirectAnalysis), - typeof(HereDocumentAnalysis), - typeof(RedirectSource), + Substitutions = substitutions, + ExecutionRegions = executionRegions, }; - var nullableProperties = new HashSet(StringComparer.Ordinal) + substitutions[0] = new CommandSubstitutionSyntax(); + executionRegions[0] = new ExecutionRegionSyntax(); + Assert.Same(substitution, Assert.Single(command.Substitutions)); + Assert.Same(executionRegion, Assert.Single(command.ExecutionRegions)); + + var stage = new SimpleCommandSyntax(); + var stages = new ShellSyntaxNode[] { stage }; + var pipeline = new PipelineSyntax { Stages = stages }; + stages[0] = new SimpleCommandSyntax(); + Assert.Same(stage, Assert.Single(pipeline.Stages)); + + var item = new CommandListItemSyntax(); + var items = new[] { item }; + var commandList = new CommandListSyntax { Items = items }; + items[0] = new CommandListItemSyntax(); + Assert.Same(item, Assert.Single(commandList.Items)); + + var frame = new CommandAncestryFrame(); + var ancestry = new[] { frame }; + var analyzed = new AnalyzedArgument(); + var arguments = new[] { analyzed }; + var redirect = new UnresolvedRedirectAnalysis(); + var redirects = new RedirectAnalysis[] { redirect }; + var occurrence = new CommandOccurrence { - $"{nameof(ParsedCommand)}.{nameof(ParsedCommand.UnparseableReason)}", - $"{nameof(ConditionalSyntax)}.{nameof(ConditionalSyntax.Else)}", - $"{nameof(ShellValueDomain)}.{nameof(ShellValueDomain.Pattern)}", - $"{nameof(ShellValueDomain)}.{nameof(ShellValueDomain.CoveringDirectory)}", - $"{nameof(RedirectAnalysis)}.{nameof(RedirectAnalysis.HereDocument)}", + Ancestry = ancestry, + Arguments = arguments, + Redirects = redirects, }; - var context = new NullabilityInfoContext(); - var visited = new HashSet(StringComparer.Ordinal); - - foreach (var type in types) + ancestry[0] = new CommandAncestryFrame(); + arguments[0] = new AnalyzedArgument(); + redirects[0] = new UnresolvedRedirectAnalysis(); + Assert.Same(frame, Assert.Single(occurrence.Ancestry)); + Assert.Same(analyzed, Assert.Single(occurrence.Arguments)); + Assert.Same(redirect, Assert.Single(occurrence.Redirects)); + + var occurrences = new[] { occurrence }; + var parsed = new ParsedCommand { Commands = occurrences }; + occurrences[0] = new CommandOccurrence(); + Assert.Same(occurrence, Assert.Single(parsed.Commands)); + + var values = new[] { "a", "b" }; + var finite = new ShellValueDomain.FiniteSet(values); + values[0] = "changed"; + Assert.Equal(new[] { "a", "b" }, finite.Values); + + var readOnlyLists = new object[] { - var properties = type.GetProperties( - BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - foreach (var property in properties.Where( - property => property.Name != "EqualityContract" && - !property.PropertyType.IsValueType)) - { - var key = $"{type.Name}.{property.Name}"; - var expected = nullableProperties.Contains(key) - ? NullabilityState.Nullable - : NullabilityState.NotNull; - var nullability = context.Create(property); - Assert.Equal(expected, nullability.ReadState); - if (property.CanWrite) - { - Assert.Equal(expected, nullability.WriteState); - } - - AssertGenericArgumentsAreNotNull(key, nullability); - visited.Add(key); - } - } - - Assert.All(nullableProperties, property => Assert.Contains(property, visited)); - - var unknownProperty = typeof(ShellValueDomain).GetProperty( - nameof(ShellValueDomain.Unknown), - BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); - Assert.NotNull(unknownProperty); - Assert.Equal( - NullabilityState.NotNull, - context.Create(unknownProperty!).ReadState); + block.Statements, + command.Substitutions, + command.ExecutionRegions, + pipeline.Stages, + commandList.Items, + occurrence.Ancestry, + occurrence.Arguments, + occurrence.Redirects, + parsed.Commands, + finite.Values, + }; + Assert.All(readOnlyLists, collection => Assert.False(collection.GetType().IsArray)); } [Fact] - public void Analysis_limits_are_static_get_only_and_match_the_locked_values() + public void Stable_v03_enums_match_the_locked_order() { - var type = typeof(ShellAnalysisLimits); - Assert.True(type.IsPublic); - Assert.True(type.IsAbstract); - Assert.True(type.IsSealed); - - Assert.Equal(32, ShellAnalysisLimits.MaxValueCandidates); - Assert.Equal(16, ShellAnalysisLimits.MaxStructuralNesting); - Assert.Equal(5, ShellAnalysisLimits.MaxWrapperRecursionDepth); - - var properties = type.GetProperties( - BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); - Assert.Equal( - new[] - { - nameof(ShellAnalysisLimits.MaxStructuralNesting), - nameof(ShellAnalysisLimits.MaxValueCandidates), - nameof(ShellAnalysisLimits.MaxWrapperRecursionDepth), - }, - properties.Select(property => property.Name).OrderBy(name => name)); - - foreach (var property in properties) - { - Assert.True(property.GetMethod!.IsStatic); - Assert.False(property.CanWrite); - Assert.Equal(typeof(int), property.PropertyType); - } + AssertEnum("Unknown", "CurrentScope", "IsolatedScope"); + AssertEnum( + "Unknown", "DirectCall", "DotSource", "CommandArgument"); + AssertEnum( + "Unknown", "Main", "Initialization", "Begin", "Process", "End", + "Filter", "Action", "Completion"); + AssertEnum( + "Unknown", "Synchronous", "Concurrent", "Deferred"); + AssertEnum( + "Unknown", "Once", "OncePerInputObject", "ZeroOrMore"); } [Fact] - public void Parsed_command_new_members_participate_in_generated_record_behavior() + public void Parsed_command_record_behavior_includes_the_new_projections() { - var clauses = Array.Empty(); - var commands = Array.Empty(); var syntax = new ShellBlockSyntax(); - var original = new ParsedCommand + var occurrence = new CommandOccurrence(); + var commands = new[] { occurrence }; + var parsed = new ParsedCommand { - Source = "echo ok", + Source = "echo ready", Syntax = syntax, Commands = commands, - Clauses = clauses, }; - var equalCopy = original with { }; - - Assert.Equal(original, equalCopy); - Assert.Equal(original.GetHashCode(), equalCopy.GetHashCode()); - Assert.Contains($"{nameof(ParsedCommand.Syntax)} =", original.ToString()); - Assert.Contains($"{nameof(ParsedCommand.Commands)} =", original.ToString()); + var equivalent = parsed with { }; - Assert.NotEqual( - original, - original with - { - Syntax = syntax with { SourceStart = 0, SourceLength = 7 }, - }); - Assert.NotEqual( - original, - original with - { - Commands = new[] { new CommandOccurrence() }, - }); + Assert.Equal(parsed, equivalent); + Assert.Equal(parsed.GetHashCode(), equivalent.GetHashCode()); + Assert.Contains("Syntax =", parsed.ToString(), StringComparison.Ordinal); + Assert.Contains("Commands =", parsed.ToString(), StringComparison.Ordinal); + Assert.NotEqual(parsed, parsed with + { + Syntax = new ShellBlockSyntax { SourceStart = 1 }, + }); + Assert.NotEqual(parsed, parsed with { Commands = Array.Empty() }); } [Fact] - public void Default_json_is_not_a_polymorphic_parser_result_round_trip_contract() + public void Default_json_is_not_a_polymorphic_value_domain_round_trip() { - var clause = new Clause - { - Verb = new VerbChain { Tokens = new[] { "echo" } }, - }; - var parsed = new ParsedCommand - { - Source = "echo ok", - Syntax = new ShellBlockSyntax - { - Statements = new ShellSyntaxNode[] - { - new SimpleCommandSyntax { Clause = clause }, - }, - }, - Commands = new[] - { - new CommandOccurrence - { - Clause = clause, - ImmediateRole = CommandOccurrenceRole.Ordinary, - IsComplete = true, - }, - }, - Clauses = new[] { clause }, - }; + ShellValueDomain value = new ShellValueDomain.Exact("ready"); - var json = JsonSerializer.Serialize(parsed); - - Assert.Contains($"\"{nameof(ParsedCommand.Syntax)}\"", json); - Assert.Contains($"\"{nameof(ParsedCommand.Commands)}\"", json); - Assert.Contains($"\"{nameof(ParsedCommand.Clauses)}\"", json); - Assert.Throws( - () => JsonSerializer.Deserialize(json)); - var syntaxTypes = typeof(ShellSyntaxNode).Assembly - .GetExportedTypes() - .Where(type => typeof(ShellSyntaxNode).IsAssignableFrom(type)); - Assert.All( - syntaxTypes, - type => Assert.DoesNotContain( - type.CustomAttributes, - attribute => attribute.AttributeType.Namespace == - "System.Text.Json.Serialization")); - Assert.DoesNotContain( - typeof(ShellSyntaxNode).Assembly.GetReferencedAssemblies(), - assembly => assembly.Name == "System.Text.Json"); + var json = JsonSerializer.Serialize(value); + + Assert.Throws(() => + JsonSerializer.Deserialize(json)); } [Fact] - public void Unknown_numeric_enum_values_remain_detectable_for_consumer_rejection() + public void Unknown_future_enum_values_remain_detectable() { - const int unknownValue = 999; - var policySensitiveEnums = new[] + var enums = new[] { - typeof(BashInitialStateMode), - typeof(PwshDialect), - typeof(PwshInitialStateMode), - typeof(ShellSyntaxKind), + typeof(CommandOccurrenceRole), + typeof(CommandAncestryRegion), typeof(ShellGroupKind), - typeof(ConditionLoopKind), typeof(ExecutionRegionOrigin), typeof(ExecutionRegionPhase), typeof(ExecutionRegionTiming), typeof(ExecutionRegionCardinality), - typeof(CommandOccurrenceRole), - typeof(CommandAncestryRegion), - typeof(ShellValueDomainKind), + typeof(FileRedirectMode), typeof(HereDocumentExpansionMode), - typeof(RedirectSourceKind), - typeof(RedirectOperation), }; - foreach (var enumType in policySensitiveEnums) - { - var value = Enum.ToObject(enumType, unknownValue); - Assert.Equal(unknownValue, Convert.ToInt32(value)); - Assert.False(Enum.IsDefined(enumType, value)); - } + Assert.All(enums, type => Assert.False(Enum.IsDefined(type, 999))); } - private static void AssertNode( - ShellSyntaxNode instance, - ShellSyntaxKind expectedKind, + private static void AssertClosedBase( + Type type, params (string Name, Type Type)[] properties) { - var type = instance.GetType(); Assert.True(type.IsPublic); - Assert.True(type.IsSealed); + Assert.True(type.IsAbstract); AssertRecord(type); - Assert.Equal(expectedKind, instance.Kind); - Assert.Null(instance.SourceStart); - Assert.Null(instance.SourceLength); + AssertProperties(type, properties.Select(property => + (property.Name, property.Type, true)).ToArray()); - AssertGetProperty(type, nameof(ShellSyntaxNode.Kind), typeof(ShellSyntaxKind)); - foreach (var property in properties) - { - AssertInitProperty(type, property.Name, property.Type); - } + var constructor = type.GetConstructor( + BindingFlags.Instance | BindingFlags.NonPublic, + binder: null, + Type.EmptyTypes, + modifiers: null); + Assert.NotNull(constructor); + Assert.True(constructor!.IsFamilyAndAssembly); - var expectedNames = properties - .Select(property => property.Name) - .Append(nameof(ShellSyntaxNode.Kind)) - .OrderBy(name => name) - .ToArray(); - Assert.Equal(expectedNames, DeclaredPublicProperties(type)); + var ownership = type.GetProperty( + "LibraryOwnership", + BindingFlags.Instance | BindingFlags.NonPublic); + Assert.NotNull(ownership); + Assert.True(ownership!.GetMethod!.IsAbstract); + Assert.True(ownership.GetMethod.IsFamilyAndAssembly); } - private static void AssertRecordWithInitProperties( + private static void AssertResultRecord( Type type, - params (string Name, Type Type)[] properties) + params (string Name, Type Type)[] properties) => + AssertResultRecordAccessors( + type, + properties.Select(property => (property.Name, property.Type, true)).ToArray()); + + private static void AssertResultRecordAccessors( + Type type, + params (string Name, Type Type, bool HasInternalSetter)[] properties) { - Assert.True(type.IsPublic); + Assert.True(type.IsNestedPublic || type.IsPublic); Assert.True(type.IsSealed); AssertRecord(type); - foreach (var property in properties) - { - AssertInitProperty(type, property.Name, property.Type); - } - - Assert.Equal( - properties.Select(property => property.Name).OrderBy(name => name), - DeclaredPublicProperties(type)); - } - - private static string[] DeclaredPublicProperties(Type type) => - type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) - .Where(property => property.Name != "EqualityContract") - .Select(property => property.Name) - .OrderBy(name => name) - .ToArray(); - - private static void AssertRecord(Type type) - { - var clone = type.GetMethod( - "$", - BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - Assert.NotNull(clone); + AssertProperties(type, properties); + Assert.Empty(type.GetConstructors(BindingFlags.Instance | BindingFlags.Public)); } - private static void AssertGenericArgumentsAreNotNull( - string propertyPath, - NullabilityInfo nullability) + private static void AssertProperties( + Type type, + IReadOnlyList<(string Name, Type Type, bool HasInternalSetter)> expected) { - foreach (var argument in nullability.GenericTypeArguments) + var properties = type + .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly) + .OrderBy(property => property.Name) + .ToArray(); + Assert.Equal(expected.Select(property => property.Name).OrderBy(name => name), + properties.Select(property => property.Name)); + foreach (var property in expected) { - Assert.True( - argument.ReadState == NullabilityState.NotNull, - $"{propertyPath} has a nullable or oblivious generic argument."); - AssertGenericArgumentsAreNotNull(propertyPath, argument); + var actual = Assert.Single(properties, candidate => candidate.Name == property.Name); + Assert.Equal(property.Type, actual.PropertyType); + Assert.True(actual.GetMethod!.IsPublic); + var setter = actual.GetSetMethod(nonPublic: true); + if (property.HasInternalSetter) + { + Assert.NotNull(setter); + Assert.True(setter!.IsAssembly); + } + else + { + Assert.Null(setter); + } } } - private static void AssertGetProperty(Type type, string name, Type expectedType) - { - var property = type.GetProperty(name); - Assert.NotNull(property); - Assert.Equal(expectedType, property!.PropertyType); - Assert.True(property.CanRead); - Assert.False(property.CanWrite); - } - - private static void AssertInitProperty(Type type, string name, Type expectedType) + private static void AssertRecord(Type type) { - var property = type.GetProperty(name); - Assert.NotNull(property); - Assert.Equal(expectedType, property!.PropertyType); - Assert.True(property.CanRead); - Assert.True(property.CanWrite); - var setter = Assert.IsAssignableFrom(property.SetMethod); - Assert.Contains( - setter.ReturnParameter.GetRequiredCustomModifiers(), - modifier => modifier.FullName == "System.Runtime.CompilerServices.IsExternalInit"); + const BindingFlags flags = BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic; + Assert.NotNull(type.GetMethod("$", flags)); + Assert.NotNull(type.GetProperty("EqualityContract", flags)); } - private static void AssertEnum(params string[] expectedNames) - where T : struct, Enum - { - Assert.Equal(expectedNames, Enum.GetNames(typeof(T))); - var values = Enum.GetValues(typeof(T)); - Assert.Equal(expectedNames.Length, values.Length); - for (var index = 0; index < values.Length; index++) - { - Assert.Equal(index, Convert.ToInt32(values.GetValue(index))); - } - } + private static void AssertEnum(params string[] names) + where T : struct, Enum => Assert.Equal(names, Enum.GetNames(typeof(T))); } diff --git a/tools/PwshCorpusTool/CorpusJson.cs b/tools/PwshCorpusTool/CorpusJson.cs index 832aa95..e7b7287 100644 --- a/tools/PwshCorpusTool/CorpusJson.cs +++ b/tools/PwshCorpusTool/CorpusJson.cs @@ -129,12 +129,6 @@ private static void AppendSyntax( bool includeV03Assertions, bool isRootBlock = false) { - if (node.Kind == ShellSyntaxKind.Unknown) - { - throw new InvalidOperationException( - "Cannot generate corpus expectations for an unknown syntax node"); - } - var currentIndex = nodes.Count; var clause = (node as SimpleCommandSyntax)?.Clause; var forEachNode = node as ForEachSyntax; @@ -142,7 +136,7 @@ private static void AppendSyntax( var clauseIndex = clause is null ? (int?)null : FindClauseIndex(parsed, clause); var syntax = new JsonObject { - ["kind"] = node.Kind.ToString(), + ["kind"] = SyntaxKind(node), ["parentIndex"] = JsonValue.Create(parentIndex), ["region"] = region.ToString(), ["childIndex"] = JsonValue.Create(childIndex), @@ -154,10 +148,10 @@ private static void AppendSyntax( }; if (includeV03Assertions && forEachNode is not null) { - syntax["bindingName"] = forEachNode.Binding.Name; - syntax["bindingRaw"] = forEachNode.Binding.Source.Raw; - syntax["bindingSourceStart"] = forEachNode.Binding.Source.SourceStart; - syntax["bindingSourceLength"] = forEachNode.Binding.Source.SourceLength; + syntax["bindingName"] = forEachNode.BindingName; + syntax["bindingRaw"] = forEachNode.BindingSource.Raw; + syntax["bindingSourceStart"] = forEachNode.BindingSource.SourceStart; + syntax["bindingSourceLength"] = forEachNode.BindingSource.SourceLength; syntax["iterableRaw"] = forEachNode.Iterable.Raw; syntax["iterableSourceStart"] = forEachNode.Iterable.SourceStart; syntax["iterableSourceLength"] = forEachNode.Iterable.SourceLength; @@ -166,7 +160,7 @@ private static void AppendSyntax( { syntax["executionOrigin"] = executionRegionNode.Origin.ToString(); syntax["hostClauseElementIndex"] = - JsonValue.Create(executionRegionNode.HostClauseElementIndex); + JsonValue.Create(FindClauseElementIndex(parsed, executionRegionNode.HostArgument)); syntax["executionPhase"] = executionRegionNode.Phase.ToString(); syntax["executionTiming"] = executionRegionNode.Timing.ToString(); syntax["executionCardinality"] = @@ -284,74 +278,6 @@ private static void AppendSyntax( nodes, includeV03Assertions); break; - case ConditionLoopSyntax loop: - AppendSyntax( - loop.Condition, - currentIndex, - CommandAncestryRegion.Condition, - childIndex: null, - listOperator: null, - parsed, - nodes, - includeV03Assertions); - AppendSyntax( - loop.Body, - currentIndex, - CommandAncestryRegion.LoopBody, - childIndex: null, - listOperator: null, - parsed, - nodes, - includeV03Assertions); - break; - case ConditionalSyntax conditional: - for (var index = 0; index < conditional.Branches.Count; index++) - { - AppendSyntax( - conditional.Branches[index], - currentIndex, - CommandAncestryRegion.Branch, - index, - listOperator: null, - parsed, - nodes, - includeV03Assertions); - } - - if (conditional.Else is not null) - { - AppendSyntax( - conditional.Else, - currentIndex, - CommandAncestryRegion.Branch, - conditional.Branches.Count, - listOperator: null, - parsed, - nodes, - includeV03Assertions); - } - - break; - case ConditionalBranchSyntax branch: - AppendSyntax( - branch.Condition, - currentIndex, - CommandAncestryRegion.Condition, - childIndex: null, - listOperator: null, - parsed, - nodes, - includeV03Assertions); - AppendSyntax( - branch.Body, - currentIndex, - CommandAncestryRegion.Branch, - childIndex: null, - listOperator: null, - parsed, - nodes, - includeV03Assertions); - break; case CommandSubstitutionSyntax substitution: AppendSyntax( substitution.Body, @@ -398,8 +324,7 @@ private static JsonArray BuildCommands( var ancestry = new JsonArray(); foreach (var frame in command.Ancestry) { - if (frame.AncestorKind == ShellSyntaxKind.Unknown || - frame.Region == CommandAncestryRegion.Unknown) + if (frame.Region == CommandAncestryRegion.Unknown) { throw new InvalidOperationException( "Cannot generate corpus expectations for unknown command ancestry"); @@ -407,11 +332,11 @@ private static JsonArray BuildCommands( ancestry.Add(new JsonObject { - ["ancestorKind"] = frame.AncestorKind.ToString(), + ["ancestorKind"] = SyntaxKind(frame.Ancestor), ["region"] = frame.Region.ToString(), ["childIndex"] = JsonValue.Create(frame.ChildIndex), - ["sourceStart"] = JsonValue.Create(frame.SourceStart), - ["sourceLength"] = JsonValue.Create(frame.SourceLength), + ["sourceStart"] = JsonValue.Create(frame.Ancestor.SourceStart), + ["sourceLength"] = JsonValue.Create(frame.Ancestor.SourceLength), }); } @@ -424,37 +349,34 @@ private static JsonArray BuildCommands( }; if (includeV03Assertions) { - var effectiveArguments = new JsonArray(); - foreach (var effective in command.EffectiveArguments) + var arguments = new JsonArray(); + foreach (var analyzed in command.Arguments) { - effectiveArguments.Add(new JsonObject + arguments.Add(new JsonObject { - ["clauseElementIndex"] = effective.ClauseElementIndex, - ["value"] = BuildValueDomain(effective.Value), + ["clauseArgumentIndex"] = FindClauseArgumentIndex( + command.Clause, + analyzed.Argument), + ["clauseElementIndex"] = FindClauseElementIndex( + command.Clause, + analyzed.Element), + ["value"] = BuildValueDomain(analyzed.Value), }); } - commandJson["effectiveArguments"] = effectiveArguments; + commandJson["arguments"] = arguments; commandJson["workingDirectory"] = BuildValueDomain(command.WorkingDirectory); if (command.Redirects.Count > 0) { var redirects = new JsonArray(); - foreach (var redirect in command.Redirects) + for (var redirectIndex = 0; + redirectIndex < command.Redirects.Count; + redirectIndex++) { - redirects.Add(new JsonObject - { - ["redirectIndex"] = redirect.RedirectIndex, - ["sourceKind"] = redirect.Source.Kind.ToString(), - ["sourceDescriptor"] = JsonValue.Create( - redirect.Source.Descriptor), - ["operation"] = redirect.Operation.ToString(), - ["targetDescriptor"] = JsonValue.Create( - redirect.TargetDescriptor), - ["target"] = BuildValueDomain(redirect.Target), - ["isPathRelevant"] = redirect.IsPathRelevant, - ["isComplete"] = redirect.IsComplete, - }); + redirects.Add(BuildRedirectAnalysis( + command.Redirects[redirectIndex], + redirectIndex)); } commandJson["redirects"] = redirects; @@ -470,20 +392,108 @@ private static JsonArray BuildCommands( private static JsonObject BuildValueDomain(ShellValueDomain domain) { var values = new JsonArray(); - foreach (var value in domain.Values) + if (domain is ShellValueDomain.Exact exact) { - values.Add(value); + values.Add(exact.Value); + } + else if (domain is ShellValueDomain.FiniteSet finiteSet) + { + foreach (var value in finiteSet.Values) + { + values.Add(value); + } } return new JsonObject { - ["kind"] = domain.Kind.ToString(), + ["kind"] = domain switch + { + ShellValueDomain.Unknown => "Unknown", + ShellValueDomain.Exact => "Exact", + ShellValueDomain.FiniteSet => "FiniteSet", + ShellValueDomain.PathPattern => "Pattern", + _ => throw new InvalidOperationException( + $"Unknown value-domain type {domain.GetType().FullName}"), + }, ["values"] = values, - ["pattern"] = domain.Pattern, - ["coveringDirectory"] = domain.CoveringDirectory, + ["pattern"] = (domain as ShellValueDomain.PathPattern)?.Pattern, + ["coveringDirectory"] = + (domain as ShellValueDomain.PathPattern)?.CoveringDirectory, }; } + private static JsonObject BuildRedirectAnalysis( + RedirectAnalysis analysis, + int redirectIndex) + { + var result = new JsonObject + { + ["redirectIndex"] = redirectIndex, + ["kind"] = analysis.GetType().Name, + ["sourceKind"] = analysis.Source.GetType().Name, + ["sourceDescriptor"] = JsonValue.Create( + (analysis.Source as RedirectSource.Descriptor)?.Value), + ["isComplete"] = analysis.IsComplete, + }; + + switch (analysis) + { + case FileRedirectAnalysis file: + result["fileMode"] = file.Mode.ToString(); + result["value"] = BuildValueDomain(file.Target); + break; + case DescriptorDuplicateRedirectAnalysis duplicate: + result["targetDescriptor"] = duplicate.TargetDescriptor; + break; + case DescriptorMoveRedirectAnalysis move: + result["targetDescriptor"] = move.TargetDescriptor; + break; + case HereStringRedirectAnalysis hereString: + result["value"] = BuildValueDomain(hereString.Data); + break; + case HereDocumentRedirectAnalysis hereDocument: + result["hereDocument"] = new JsonObject + { + ["delimiter"] = BuildSourceFragment( + hereDocument.Document.Delimiter), + ["body"] = BuildSourceFragment(hereDocument.Document.Body), + ["expansionMode"] = + hereDocument.Document.ExpansionMode.ToString(), + ["stripLeadingTabs"] = hereDocument.Document.StripLeadingTabs, + ["isComplete"] = hereDocument.Document.IsComplete, + }; + break; + case UnresolvedRedirectAnalysis or DescriptorCloseRedirectAnalysis: + break; + default: + throw new InvalidOperationException( + $"Unknown redirect-analysis type {analysis.GetType().FullName}"); + } + + return result; + } + + private static JsonObject BuildSourceFragment(ShellSourceFragment fragment) => new() + { + ["raw"] = fragment.Raw, + ["sourceStart"] = JsonValue.Create(fragment.SourceStart), + ["sourceLength"] = JsonValue.Create(fragment.SourceLength), + }; + + private static string SyntaxKind(ShellSyntaxNode node) => node switch + { + ShellBlockSyntax => "Block", + SimpleCommandSyntax => "SimpleCommand", + PipelineSyntax => "Pipeline", + CommandListSyntax => "CommandList", + GroupSyntax => "Group", + ForEachSyntax => "ForEach", + CommandSubstitutionSyntax => "CommandSubstitution", + ExecutionRegionSyntax => "ExecutionRegion", + _ => throw new InvalidOperationException( + $"Unknown syntax-node type {node.GetType().FullName}"), + }; + private static int FindClauseIndex(ParsedCommand parsed, Clause clause) { for (var index = 0; index < parsed.Clauses.Count; index++) @@ -498,6 +508,58 @@ private static int FindClauseIndex(ParsedCommand parsed, Clause clause) "Structural corpus generation found a Clause outside ParsedCommand.Clauses"); } + private static int FindClauseArgumentIndex(Clause clause, Arg argument) + { + for (var index = 0; index < clause.Args.Count; index++) + { + if (ReferenceEquals(clause.Args[index], argument)) + { + return index; + } + } + + throw new InvalidOperationException( + "Analyzed argument was not owned by its command clause"); + } + + private static int? FindClauseElementIndex( + ParsedCommand parsed, + ClauseElement? element) + { + if (element is null) + { + return null; + } + + foreach (var clause in parsed.Clauses) + { + for (var index = 0; index < clause.Elements.Count; index++) + { + if (ReferenceEquals(clause.Elements[index], element)) + { + return index; + } + } + } + + throw new InvalidOperationException( + "Execution-region host argument was not owned by a parsed clause"); + } + + private static int FindClauseElementIndex(Clause clause, ClauseElement element) + { + for (var index = 0; index < clause.Elements.Count; index++) + { + if (ReferenceEquals(clause.Elements[index], element)) + { + return index; + } + } + + throw new InvalidOperationException( + "Analyzed element was not owned by its command clause"); + } + private static JsonObject BuildClause( Clause clause, bool includeElements,