I am currently trying to use the AST functionality introduced in PowerShell 3.0 to modify a ScriptBlock. My requirement is that all the parameters in the parameter block of the ScriptBlock get a [Parameter(Mandatory)]
attribute.
Basically the code should modify this:
Param([string]$x)
Write-Host $x
to this:
Param([Parameter(Mandatory)][string]$x)
Write-Host $x
However, I ran into a problem when adding that new attribute, since it expects an IScriptExtent
and I am not sure how I should create a new IScriptExtent
.
How can I create a new script extent? What values can I use for the position? Do I have to change the position of all following extents?
I tried just reusing the extent of each parameter I am modifying, but unfortunately this does not seem to yield the results it should (e.g. when I am calling ToString
on the modified ScriptBlock
I don't see any changes).
My implementation so far is based on the ICustomAstVisitor
found here.
The most important method looks like this:
public object VisitParameter(ParameterAst parameterAst)
{
var newName = VisitElement(parameterAst.Name);
var extent = // What to do here?
var mandatoryArg = new AttributeAst(extent, new ReflectionTypeName(typeof (ParameterAttribute)),
new ExpressionAst[0],
new[] {new NamedAttributeArgumentAst(extent, "Mandatory", new ConstantExpressionAst(extent, true), true)});
var newAttributes = new[] {mandatoryArg}.Concat(VisitElements(parameterAst.Attributes));
var newDefaultValue = VisitElement(parameterAst.DefaultValue);
return new ParameterAst(parameterAst.Extent, newName, newAttributes, newDefaultValue);
}
The script extent is used primarily for error reporting, but is also used for debugging (for example, setting a line breakpoint.)
In general, the options for synthesized script (like your example) are:
In your example, any of the above are suitable. The second option is the simplest. The third option is just a variant of the second, but you would set the content to something useful, e.g.
<#Generated: [Parameter(Mandatory)] #>