< Summary - Kestrun — Combined Coverage

Information
Class: Private.Variable.Get-KrAssignedVariables
Assembly: Kestrun.PowerShell.Private
File(s): /home/runner/work/Kestrun/Kestrun/src/PowerShell/Kestrun/Private/Variable/Get-KrAssignedVariables.ps1
Tag: Kestrun/Kestrun@2d87023b37eb91155071c91dd3d6a2eeb3004705
Line coverage
0%
Covered lines: 0
Uncovered lines: 87
Coverable lines: 87
Total lines: 185
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 09/03/2025 - 13:55:51 Line coverage: 64.4% (49/76) Total lines: 170 Tag: Kestrun/Kestrun@80ce2a54be2f719c7be1c21a92a8156bfdc48eb409/04/2025 - 17:02:01 Line coverage: 67.4% (58/86) Total lines: 184 Tag: Kestrun/Kestrun@f3880b25ea131298aa2f8b1e0d0a8d55eb160bc009/04/2025 - 18:11:31 Line coverage: 67.4% (58/86) Total lines: 185 Tag: Kestrun/Kestrun@de99e24698289f3f61ac7b73e96092732ae12b0509/12/2025 - 03:43:11 Line coverage: 67.4% (58/86) Total lines: 184 Tag: Kestrun/Kestrun@d160286e3020330b1eb862d66a37db2e26fc904209/15/2025 - 19:16:35 Line coverage: 67.8% (59/87) Total lines: 185 Tag: Kestrun/Kestrun@bfb58693b9baaed61644ace5b29e014d9ffacbc910/13/2025 - 16:52:37 Line coverage: 0% (0/87) Total lines: 185 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e

Metrics

File(s)

/home/runner/work/Kestrun/Kestrun/src/PowerShell/Kestrun/Private/Variable/Get-KrAssignedVariables.ps1

#LineLine coverage
 1<#
 2    .SYNOPSIS
 3      Find variables that are *defined/assigned* in a scriptblock and (optionally) fetch their values.
 4    .DESCRIPTION
 5      Scans the AST for AssignmentStatementAst where the LHS is a VariableExpressionAst,
 6      and optionally Set-Variable/New-Variable calls. Can resolve current values from
 7      the appropriate scope (local/script/global) in the *current* runspace.
 8    .PARAMETER ScriptBlock
 9      ScriptBlock to scan. If omitted, use -Current to inspect the currently running block.
 10    .PARAMETER FromParent
 11      Inspect the *currently executing* scriptblock (via $MyInvocation.MyCommand.ScriptBlock).
 12    .PARAMETER IncludeSetVariable
 13      Also detect variables created via Set-Variable/New-Variable (-Name … [-Scope …]).
 14    .PARAMETER ResolveValues
 15      Resolve current values from variable:<scope>:<name> and include Type/Value.
 16    .PARAMETER DefaultScope
 17      Scope to assume when the LHS had no explicit scope (default: Local).
 18#>
 19function Get-KrAssignedVariable {
 20    [CmdletBinding(DefaultParameterSetName = 'Given')]
 21    param(
 22        [Parameter(ParameterSetName = 'Given', Position = 0)]
 23        [scriptblock]$ScriptBlock,
 24
 25        # NEW: use the caller's scriptblock (parent frame)
 26        [Parameter(ParameterSetName = 'FromParent', Mandatory)]
 27        [switch]$FromParent,
 28
 29        # How many frames up to climb (1 = immediate caller)
 30        [Parameter(ParameterSetName = 'FromParent')]
 31        [int]$Up = 1,
 32
 33        # Optional: skip frames from these modules when searching
 34        [Parameter(ParameterSetName = 'FromParent')]
 035        [string[]]$ExcludeModules = @('Kestrun'),
 36
 37        [switch]$IncludeSetVariable,
 38        [switch]$ResolveValues,
 39        [ValidateSet('Local', 'Script', 'Global')]
 40        [string]$DefaultScope = 'Script'
 41    )
 42
 43    # ---------- resolve $ScriptBlock source ----------
 044    if ($FromParent.IsPresent) {
 045        $allFrames = Get-PSCallStack
 46        # 0 = this function, 1 = immediate caller, 2+ = higher parents
 047        $frames = $allFrames | Select-Object -Skip 1
 48
 049        if ($ExcludeModules.Count) {
 050            $frames = $frames | Where-Object {
 051                $mn = $_.InvocationInfo.MyCommand.ModuleName
 052                -not ($mn -and ($mn -in $ExcludeModules))
 53            }
 54        }
 55
 56        # pick the desired parent frame
 057        $frame = $frames | Select-Object -Skip ($Up - 1) -First 1
 058        if (-not $frame) { throw "No parent frame found (Up=$Up)." }
 59
 60        # Figure out how far “up” that is compared to the original call stack
 061        $scopeUp = ($allFrames.IndexOf($frame)) + 1
 062        if ($scopeUp -lt 1) { throw "Parent frame not found." }
 63
 64        # prefer its live ScriptBlock; if null, rebuild from file
 065        $ScriptBlock = $frame.InvocationInfo.MyCommand.ScriptBlock
 066        if (-not $ScriptBlock -and $frame.ScriptName) {
 067            $ScriptBlock = [scriptblock]::Create((Get-Content -Raw -LiteralPath $frame.ScriptName))
 68        }
 069        if (-not $ScriptBlock) { throw "Parent frame has no scriptblock or script file to parse." }
 70    }
 71
 072    if (-not $ScriptBlock) {
 073        throw "No scriptblock provided. Use -FromParent or pass a ScriptBlock."
 74    }
 075    $ast = ($ScriptBlock.Ast).ToString()
 76
 077    $endstring = $ast.IndexOf("Enable-KrConfiguration", [StringComparison]::OrdinalIgnoreCase)
 078    if ($endstring -lt 0) {
 079        throw "The provided scriptblock does not appear to contain 'Enable-KrConfiguration' call."
 80    }
 081    $ast = $ast.Substring(0, $endstring).Trim()
 082    if ($ast.StartsWith('{')) {
 083        $ast += "`n}"
 84    }
 085    $ScriptBlock = [scriptblock]::Create($ast)
 86
 87
 88    <#
 89   .SYNOPSIS
 90       Checks if a given AST node is inside a function.
 91   .DESCRIPTION
 92       This function traverses the parent nodes of the given AST node to determine if it is
 93       located within a function definition.
 94    .PARAMETER node
 95       The AST node to check.
 96    .OUTPUTS
 97       [bool] Returns true if the node is inside a function, false otherwise.
 98   #>
 99    function _IsInFunction([System.Management.Automation.Language.Ast] $node) {
 0100        $p = $node.Parent
 0101        while ($p) {
 0102            if ($p -is [System.Management.Automation.Language.FunctionDefinitionAst]) { return $true }
 0103            $p = $p.Parent
 104        }
 0105        return $false
 106    }
 107
 0108    $assignAsts = $ScriptBlock.Ast.FindAll(
 0109        { param($n) $n -is [System.Management.Automation.Language.AssignmentStatementAst] }, $true)
 110
 0111    foreach ($a in $assignAsts) {
 0112        $varAst = $a.Left.Find(
 0113            { param($n) $n -is [System.Management.Automation.Language.VariableExpressionAst] }, $true
 0114        ) | Select-Object -First 1
 0115        if (-not $varAst) { continue }
 116
 0117        $vp = $varAst.VariablePath
 0118        $name = $vp.UnqualifiedPath
 119
 0120        if (-not $name) { $name = $vp.UserPath }            # ← fallback
 0121        if (-not $name) { continue }
 0122        $name = $name -replace '^[^:]*:', ''
 0123        if ($name.Contains('.') -or $name.Contains('[')) { continue }
 0124        if ($name.StartsWith('{') -and $name.EndsWith('}')) {
 0125            $name = $name.Substring(1, $name.Length - 2)        # ← ${foo} → foo
 126        }
 0127        $val = Get-Variable -Name $name -Scope $scopeUp -ValueOnly -ErrorAction SilentlyContinue
 0128        $type = if ($null -ne $val) { $val.GetType().FullName } else { $null }
 129
 0130        [pscustomobject]@{
 0131            Name = $name
 0132            ScopeHint = $scope
 0133            ProviderPath = $provider
 0134            Source = 'Assignment'
 0135            Operator = $a.Operator.ToString()
 0136            Type = $type
 0137            Value = $val
 138        }
 139    }
 140
 0141    if ($IncludeSetVariable) {
 0142        $cmdAsts = $ScriptBlock.Ast.FindAll(
 0143            { param($n) $n -is [System.Management.Automation.Language.CommandAst] -and -not (_IsInFunction $n) }, $true)
 144
 0145        foreach ($c in $cmdAsts) {
 0146            $cmd = $c.GetCommandName()
 0147            if ($cmd -notin 'Set-Variable', 'New-Variable') { continue }
 0148            $named = @{}
 0149            foreach ($e in $c.CommandElements) {
 0150                if ($e -is [System.Management.Automation.Language.CommandParameterAst] -and $e.ParameterName -in 'Name',
 0151                    $arg = $e.Argument
 0152                    if ($arg -is [System.Management.Automation.Language.StringConstantExpressionAst]) {
 0153                        $named[$e.ParameterName] = $arg.Value
 154                    }
 155                }
 156            }
 0157            if ($named.ContainsKey('Name')) {
 0158                $name = $named['Name']
 0159                $scope = $named['Scope'] ?? $DefaultScope
 0160                $provider = "variable:$($scope):$name"
 0161                $val = $null; $type = $null
 0162                if ($ResolveValues) {
 163                    try {
 0164                        $val = (Get-Item -EA SilentlyContinue $provider).Value
 0165                        if ($null -ne $val) { $type = $val.GetType().FullName }
 166                    } catch {
 0167                        Write-Warning "Failed to resolve variable '$name' in scope '$scope': $_"
 168                    }
 169                }
 0170                [pscustomobject]@{
 0171                    Name = $name
 0172                    ScopeHint = $scope
 0173                    ProviderPath = $provider
 0174                    Source = $cmd
 0175                    Operator = $null
 0176                    Type = $type
 0177                    Value = $val
 0178                } | ForEach-Object { [void]$rows.Add($_) }
 179            }
 180        }
 181    }
 182
 183    # keep last occurrence per (ScopeHint, Name)
 0184    $rows | Group-Object ScopeHint, Name | ForEach-Object { $_.Group[-1] }
 185}