-
-
Notifications
You must be signed in to change notification settings - Fork 477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for excluding functions from coverage with attribute #2593
base: main
Are you sure you want to change the base?
Add support for excluding functions from coverage with attribute #2593
Conversation
If the approach is valid, I'll refine the tests and add proper documentation. Feedback is welcome! |
99eb66a
to
1c9dd96
Compare
…ibute()] attribute
1c9dd96
to
d1c84df
Compare
53c13ac
to
8608926
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for looking into this! It works as intended, though I've suggested some improvements.
While reviewing I realized we could make this more efficient, see comment about FindAll
. So I'd like to discuss an alternative approach. See draft PR #2598 which should produce the same results.
) | ||
|
||
for ($parent = $Ast.Parent; $null -ne $parent; $parent = $parent.Parent) { | ||
if ($parent -is [System.Management.Automation.Language.FunctionDefinitionAst]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please replace FunctionDefinitionAst
with ScriptBlockAst
in the logic so we can exclude whole scripts and other scriptblocks. Besides type change, you'll probably only need to remove .Body.
in Get-AttributeNames
.
# Script: ScriptBlockAst -> ParamBlockAst -> Attributes
$ast = {
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
param($a, $b)
$a + $b
}.Ast
# Function: FunctionDefinitionAst -> ScriptBlockAst (Body) -> ParamBlockAst -> Attributes
$ast = {
function t {
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
param($a, $b)
$a + $b
}
}.Ast
# Adv function: FunctionDefinitionAst -> ScriptBlockAst (Body) -> ParamBlockAst -> Attributes
$ast = {
function t {
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
param($a, $b)
begin { $a + $b }
process {}
end {}
}
}.Ast
# Random scriptblock: ScriptBlockAst -> ParamBlockAst -> Attributes
$ast = {
1..10 | ForEach-Object {
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
param($a, $b)
$a + $b
}
}.Ast
@@ -297,17 +297,101 @@ function Get-CommandsInFile { | |||
$args[0] -is [System.Management.Automation.Language.BreakStatementAst] -or | |||
$args[0] -is [System.Management.Automation.Language.ContinueStatementAst] -or | |||
$args[0] -is [System.Management.Automation.Language.ExitStatementAst] -or | |||
$args[0] -is [System.Management.Automation.Language.ThrowStatementAst] | |||
$args[0] -is [System.Management.Automation.Language.ThrowStatementAst] -and | |||
-not (IsExcludedByAttribute -Ast $args[0] -TargetAttribute 'System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FindAll
will trigger this for commands in nested scriptblocks even though a parent is excluded. Not ideal to reverse (check parents) on every step.
$ast = {
1 | ForEach-Object {
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
param($a, $b)
$a + $b
2 | ForEach-Object {
3 | ForEach-Object {
continue
}
}
}
}.Ast
$ast.FindAll($predicate, $searchNestedScriptBlocks)
# Processing .. output added in IsExcludedByAttribute
Processing 1
Processing ForEach-Object {
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
param($a, $b)
$a + $b
2 | ForEach-Object {
3 | ForEach-Object {
continue
}
}
}
Processing $a + $b
Processing 2
Processing ForEach-Object {
3 | ForEach-Object {
continue
}
}
Processing 3
Processing ForEach-Object {
continue
}
Processing continue
Expression Redirections Extent
---------- ------------ ------
1 {} 1
{} ForEach-Object {…
We could avoid this by rewriting to use an AstVisitor
, stopping early at a ScriptBlockAst where the attribute is set.
|
||
return @() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return @() |
nit: unnecessary?
$topParent = Get-AstTopParent -Ast $Ast | ||
|
||
if ($null -eq $topParent) { | ||
return @() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return @() | |
return |
nit: unnecessary?
foreach ($usingStatement in $usingStatements) { | ||
$namespaces += $usingStatement.Name.Value | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
foreach ($usingStatement in $usingStatements) { | |
$namespaces += $usingStatement.Name.Value | |
} | |
$namespaces = foreach ($usingStatement in $usingStatements) { | |
$usingStatement.Name.Value | |
} |
more efficient to output directly or capture foreach {}
[System.Management.Automation.Language.Ast] $Ast | ||
) | ||
|
||
$namespaces = @() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$namespaces = @() |
more efficient to output directly or capture foreach {}
PR Summary
Fixes #2268
This pull request introduces support for excluding functions from code coverage analysis using the
[ExcludeFromCodeCoverageAttribute()]
attribute.Changes:
Get-CommandsInFile
function to skip AST nodes within functions marked with[ExcludeFromCodeCoverageAttribute()]
.IsExcludedByAttribute
to check if a function has the exclusion attribute applied, either fully qualified or resolved through using namespace.Get-NamespacesFromScript
to identify and apply using namespace declarations for resolving unqualified attribute names (not sure if similar logic already exists in the codebase).PR Checklist
Create Pull Request
to mark it as a draft. PR can be markedReady for review
when it's ready.