132 lines
4.0 KiB
PowerShell
132 lines
4.0 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[string[]]$Path = @(
|
|
'crates/common/src/formats',
|
|
'crates/toon/src',
|
|
'crates/ison/src',
|
|
'crates/isonl/src',
|
|
'crates/zon/src',
|
|
'crates/tonl/src'
|
|
)
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Set-StrictMode -Version Latest
|
|
|
|
$workspaceRoot = Split-Path -Parent $PSScriptRoot
|
|
Push-Location -LiteralPath $workspaceRoot
|
|
|
|
function Get-BraceDelta {
|
|
param(
|
|
[AllowEmptyString()]
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Line
|
|
)
|
|
|
|
$open = ([regex]::Matches($Line, '\{')).Count
|
|
$close = ([regex]::Matches($Line, '\}')).Count
|
|
return $open - $close
|
|
}
|
|
|
|
function Test-AllowedLine {
|
|
param(
|
|
[AllowEmptyString()]
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Line,
|
|
[AllowEmptyString()]
|
|
[string]$PreviousLine
|
|
)
|
|
|
|
$sameLineComment = $Line -match '(^|\s)//\s*jade:\s*allow-panic\s+because:\s+\S'
|
|
$previousLineComment = -not [string]::IsNullOrWhiteSpace($PreviousLine) -and
|
|
$PreviousLine -match '^\s*//\s*jade:\s*allow-panic\s+because:\s+\S'
|
|
|
|
return $sameLineComment -or (
|
|
-not [string]::IsNullOrWhiteSpace($PreviousLine) -and
|
|
$previousLineComment
|
|
)
|
|
}
|
|
|
|
$patterns = @(
|
|
'panic!\s*\(',
|
|
'\.unwrap\s*\(',
|
|
'\.expect\s*\('
|
|
)
|
|
$failures = [System.Collections.Generic.List[string]]::new()
|
|
|
|
try {
|
|
foreach ($root in $Path) {
|
|
if (-not (Test-Path -LiteralPath $root)) {
|
|
throw "No-panic scan path does not exist: $root"
|
|
}
|
|
|
|
$files = Get-ChildItem -LiteralPath $root -Recurse -File -Filter '*.rs'
|
|
foreach ($file in $files) {
|
|
if (($file.FullName -split '[\\/]') -contains 'tests') {
|
|
continue
|
|
}
|
|
|
|
$lines = Get-Content -LiteralPath $file.FullName
|
|
$pendingTestModule = $false
|
|
$insideTestModule = $false
|
|
$braceDepth = 0
|
|
$previousLine = ''
|
|
for ($index = 0; $index -lt $lines.Count; $index += 1) {
|
|
$line = [string]$lines[$index]
|
|
$trimmed = $line.TrimStart()
|
|
|
|
if ($insideTestModule) {
|
|
$braceDepth += Get-BraceDelta -Line $line
|
|
if ($braceDepth -le 0) {
|
|
$insideTestModule = $false
|
|
$braceDepth = 0
|
|
}
|
|
$previousLine = $line
|
|
continue
|
|
}
|
|
|
|
if ($trimmed.StartsWith('#[cfg(test)]')) {
|
|
$pendingTestModule = $true
|
|
$previousLine = $line
|
|
continue
|
|
}
|
|
|
|
if ($pendingTestModule -and $trimmed -match '^mod\s+tests\s*\{') {
|
|
$insideTestModule = $true
|
|
$braceDepth = Get-BraceDelta -Line $line
|
|
$pendingTestModule = $false
|
|
$previousLine = $line
|
|
continue
|
|
}
|
|
if (-not $trimmed.StartsWith('#[')) {
|
|
$pendingTestModule = $false
|
|
}
|
|
|
|
if ($trimmed.StartsWith('//')) {
|
|
$previousLine = $line
|
|
continue
|
|
}
|
|
|
|
foreach ($pattern in $patterns) {
|
|
if ($line -match $pattern -and -not (Test-AllowedLine -Line $line -PreviousLine $previousLine)) {
|
|
$relative = Resolve-Path -LiteralPath $file.FullName -Relative
|
|
$failures.Add("${relative}:$($index + 1): panic surface requires removal, fallible propagation, or a strict '// jade: allow-panic because: <reason>' comment directly beside this call")
|
|
}
|
|
}
|
|
|
|
$previousLine = $line
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($failures.Count -gt 0) {
|
|
$failures | ForEach-Object { Write-Error $_ }
|
|
throw "No-panic gate found $($failures.Count) unapproved panic surface(s)."
|
|
}
|
|
|
|
Write-Host "No-panic gate passed for $($Path.Count) root(s)."
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|