Skip to content

Commit 0c1a1cc

Browse files
author
Quoc Truong
committed
Add Scope and Target argument to rule suppression
1 parent 93f1a54 commit 0c1a1cc

File tree

5 files changed

+151
-9
lines changed

5 files changed

+151
-9
lines changed

Engine/Commands/InvokeScriptAnalyzerCommand.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,6 @@ private void AnalyzeFile(string filePath)
257257
ParseError[] errors = null;
258258
List<DiagnosticRecord> diagnostics = new List<DiagnosticRecord>();
259259

260-
IEnumerable<Ast> funcDefAsts;
261-
262260
// Use a List of KVP rather than dictionary, since for a script containing inline functions with same signature, keys clash
263261
List<KeyValuePair<CommandInfo, IScriptExtent>> cmdInfoTable = new List<KeyValuePair<CommandInfo, IScriptExtent>>();
264262

Engine/Generic/RuleSuppression.cs

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using System.Management.Automation.Language;
44
using System.Collections.Generic;
5+
using System.Text.RegularExpressions;
56

67
namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic
78
{
@@ -46,6 +47,24 @@ public string RuleSuppressionID
4647
set;
4748
}
4849

50+
/// <summary>
51+
/// Scope of the rule suppression
52+
/// </summary>
53+
public string Scope
54+
{
55+
get;
56+
set;
57+
}
58+
59+
/// <summary>
60+
/// Target of the rule suppression
61+
/// </summary>
62+
public string Target
63+
{
64+
get;
65+
set;
66+
}
67+
4968
/// <summary>
5069
/// Returns error occurred in trying to parse the attribute
5170
/// </summary>
@@ -55,6 +74,18 @@ public string Error
5574
set;
5675
}
5776

77+
private static HashSet<string> scopeSet;
78+
79+
/// <summary>
80+
/// Initialize the scopeSet
81+
/// </summary>
82+
static RuleSuppression()
83+
{
84+
scopeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
85+
scopeSet.Add("function");
86+
scopeSet.Add("class");
87+
}
88+
5889
/// <summary>
5990
/// Returns rule suppression from an attribute ast that has the type suppressmessageattribute
6091
/// </summary>
@@ -85,6 +116,14 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
85116
{
86117
switch (count)
87118
{
119+
case 4:
120+
Target = (positionalArguments[3] as StringConstantExpressionAst).Value;
121+
goto case 3;
122+
123+
case 3:
124+
Scope = (positionalArguments[2] as StringConstantExpressionAst).Value;
125+
goto case 2;
126+
88127
case 2:
89128
RuleSuppressionID = (positionalArguments[1] as StringConstantExpressionAst).Value;
90129
goto case 1;
@@ -134,17 +173,62 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
134173
RuleSuppressionID = (name.Argument as StringConstantExpressionAst).Value;
135174
goto default;
136175

176+
case "scope":
177+
if (!String.IsNullOrWhiteSpace(Scope))
178+
{
179+
Error = String.Format(Strings.NamedAndPositionalArgumentsConflictError, name);
180+
}
181+
182+
Scope = (name.Argument as StringConstantExpressionAst).Value;
183+
184+
if (!scopeSet.Contains(Scope))
185+
{
186+
Error = Strings.WrongScopeArgumentSuppressionAttributeError;
187+
}
188+
189+
goto default;
190+
191+
case "target":
192+
if (!String.IsNullOrWhiteSpace(Target))
193+
{
194+
Error = String.Format(Strings.NamedAndPositionalArgumentsConflictError, name);
195+
}
196+
197+
Target = (name.Argument as StringConstantExpressionAst).Value;
198+
goto default;
199+
137200
default:
138201
break;
139202
}
140203
}
141204
}
205+
206+
// Must have scope and target together
207+
if (String.IsNullOrWhiteSpace(Scope) && !String.IsNullOrWhiteSpace(Target))
208+
{
209+
Error = Strings.TargetWithoutScopeSuppressionAttributeError;
210+
}
142211
}
143212

144213
StartOffset = start;
145214
EndOffset = end;
146215
}
147216

217+
/// <summary>
218+
/// Constructs rule expression from rule name, id, start and end
219+
/// </summary>
220+
/// <param name="ruleName"></param>
221+
/// <param name="ruleSuppressionID"></param>
222+
/// <param name="start"></param>
223+
/// <param name="end"></param>
224+
public RuleSuppression(string ruleName, string ruleSuppressionID, int start, int end)
225+
{
226+
RuleName = ruleName;
227+
RuleSuppressionID = ruleSuppressionID;
228+
StartOffset = start;
229+
EndOffset = end;
230+
}
231+
148232
/// <summary>
149233
/// Given a list of attribute asts, return a list of rule suppression
150234
/// with startoffset at start and endoffset at end
@@ -153,11 +237,11 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
153237
/// <param name="start"></param>
154238
/// <param name="end"></param>
155239
/// <returns></returns>
156-
public static List<RuleSuppression> GetSuppressions(IEnumerable<AttributeAst> attrAsts, int start, int end)
240+
public static List<RuleSuppression> GetSuppressions(IEnumerable<AttributeAst> attrAsts, int start, int end, Ast scopeAst)
157241
{
158242
List<RuleSuppression> result = new List<RuleSuppression>();
159243

160-
if (attrAsts == null)
244+
if (attrAsts == null || scopeAst == null)
161245
{
162246
return result;
163247
}
@@ -169,8 +253,44 @@ public static List<RuleSuppression> GetSuppressions(IEnumerable<AttributeAst> at
169253
{
170254
RuleSuppression ruleSupp = new RuleSuppression(attributeAst, start, end);
171255

172-
if (string.IsNullOrWhiteSpace(ruleSupp.Error))
256+
// If there is no error and scope is not null
257+
if (String.IsNullOrWhiteSpace(ruleSupp.Error) && !String.IsNullOrWhiteSpace(ruleSupp.Scope))
258+
{
259+
if (String.IsNullOrWhiteSpace(ruleSupp.Target))
260+
{
261+
ruleSupp.Target = "*";
262+
}
263+
264+
// regex for wild card *
265+
Regex reg = new Regex(String.Format("^{0}$", Regex.Escape(ruleSupp.Target).Replace(@"\*", ".*")));
266+
IEnumerable<Ast> targetAsts = null;
267+
268+
switch (ruleSupp.Scope.ToLower())
269+
{
270+
case "function":
271+
targetAsts = scopeAst.FindAll(item => item is FunctionDefinitionAst && reg.IsMatch((item as FunctionDefinitionAst).Name), true);
272+
goto default;
273+
274+
case "class":
275+
targetAsts = scopeAst.FindAll(item => item is TypeDefinitionAst && reg.IsMatch((item as TypeDefinitionAst).Name), true);
276+
goto default;
277+
278+
default:
279+
break;
280+
}
281+
282+
if (targetAsts != null)
283+
{
284+
foreach (Ast targetAst in targetAsts)
285+
{
286+
result.Add(new RuleSuppression(ruleSupp.RuleName, ruleSupp.RuleSuppressionID, targetAst.Extent.StartOffset, targetAst.Extent.EndOffset));
287+
}
288+
}
289+
290+
}
291+
else
173292
{
293+
// this may add rule suppression that contains erro but we will check for this in the engine to throw out error
174294
result.Add(ruleSupp);
175295
}
176296
}

Engine/Helper.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ public Dictionary<string, List<RuleSuppression>> GetRuleSuppression(Ast ast)
627627
// Get rule suppression from the ast itself if it is scriptblockast
628628
if (sbAst != null && sbAst.ParamBlock != null && sbAst.ParamBlock.Attributes != null)
629629
{
630-
ruleSuppressionList.AddRange(RuleSuppression.GetSuppressions(sbAst.ParamBlock.Attributes, sbAst.Extent.StartOffset, sbAst.Extent.EndOffset));
630+
ruleSuppressionList.AddRange(RuleSuppression.GetSuppressions(sbAst.ParamBlock.Attributes, sbAst.Extent.StartOffset, sbAst.Extent.EndOffset, sbAst));
631631
}
632632

633633
// Get rule suppression from functions
@@ -674,7 +674,7 @@ internal List<RuleSuppression> GetSuppressionsFunction(FunctionDefinitionAst fun
674674
if (funcAst != null && funcAst.Body != null
675675
&& funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Attributes != null)
676676
{
677-
result.AddRange(RuleSuppression.GetSuppressions(funcAst.Body.ParamBlock.Attributes, funcAst.Extent.StartOffset, funcAst.Extent.EndOffset));
677+
result.AddRange(RuleSuppression.GetSuppressions(funcAst.Body.ParamBlock.Attributes, funcAst.Extent.StartOffset, funcAst.Extent.EndOffset, funcAst));
678678
}
679679

680680
return result;
@@ -691,7 +691,7 @@ internal List<RuleSuppression> GetSuppressionsClass(TypeDefinitionAst typeAst)
691691

692692
if (typeAst != null && typeAst.Attributes != null && typeAst.Attributes.Count != 0)
693693
{
694-
result.AddRange(RuleSuppression.GetSuppressions(typeAst.Attributes, typeAst.Extent.StartOffset, typeAst.Extent.EndOffset));
694+
result.AddRange(RuleSuppression.GetSuppressions(typeAst.Attributes, typeAst.Extent.StartOffset, typeAst.Extent.EndOffset, typeAst));
695695
}
696696

697697
if (typeAst.Members == null)
@@ -708,7 +708,7 @@ internal List<RuleSuppression> GetSuppressionsClass(TypeDefinitionAst typeAst)
708708
continue;
709709
}
710710

711-
result.AddRange(RuleSuppression.GetSuppressions(funcMemb.Attributes, funcMemb.Extent.StartOffset, funcMemb.Extent.EndOffset));
711+
result.AddRange(RuleSuppression.GetSuppressions(funcMemb.Attributes, funcMemb.Extent.StartOffset, funcMemb.Extent.EndOffset, funcMemb));
712712
}
713713

714714
return result;

Engine/Strings.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Engine/Strings.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,16 @@
165165
<data name="StringConstantArgumentsSuppressionAttributeError" xml:space="preserve">
166166
<value>All the arguments of the suppression message attribute should be string constants.</value>
167167
</data>
168+
<data name="TargetWithoutScopeSuppressionAttributeError" xml:space="preserve">
169+
<value>For Suppression Message Attribute, if Target is specified, Scope must be specified.</value>
170+
</data>
168171
<data name="VerboseFileMessage" xml:space="preserve">
169172
<value>Analyzing file: {0}</value>
170173
</data>
171174
<data name="VerboseRunningMessage" xml:space="preserve">
172175
<value>Running {0} rule.</value>
173176
</data>
177+
<data name="WrongScopeArgumentSuppressionAttributeError" xml:space="preserve">
178+
<value>Scope can only be either function or class.</value>
179+
</data>
174180
</root>

0 commit comments

Comments
 (0)