22using System . Linq ;
33using System . Management . Automation . Language ;
44using System . Collections . Generic ;
5+ using System . Text . RegularExpressions ;
6+ using System . Globalization ;
57
68namespace Microsoft . Windows . Powershell . ScriptAnalyzer . Generic
79{
@@ -10,6 +12,8 @@ namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic
1012 /// </summary>
1113 public class RuleSuppression
1214 {
15+ private string _ruleName ;
16+
1317 /// <summary>
1418 /// The start offset of the rule suppression
1519 /// </summary>
@@ -33,8 +37,24 @@ public int EndOffset
3337 /// </summary>
3438 public string RuleName
3539 {
36- get ;
37- set ;
40+ get
41+ {
42+ return _ruleName ;
43+ }
44+
45+ set
46+ {
47+ _ruleName = value ;
48+ if ( ( ScriptAnalyzer . Instance . ScriptRules != null
49+ && ScriptAnalyzer . Instance . ScriptRules . Count ( item => String . Equals ( item . GetName ( ) , _ruleName , StringComparison . OrdinalIgnoreCase ) ) == 0 )
50+ && ( ScriptAnalyzer . Instance . TokenRules != null
51+ && ScriptAnalyzer . Instance . TokenRules . Count ( item => String . Equals ( item . GetName ( ) , _ruleName , StringComparison . OrdinalIgnoreCase ) ) == 0 )
52+ && ( ScriptAnalyzer . Instance . ExternalRules != null
53+ && ScriptAnalyzer . Instance . ExternalRules . Count ( item => String . Equals ( item . GetName ( ) , _ruleName , StringComparison . OrdinalIgnoreCase ) ) == 0 ) )
54+ {
55+ Error = String . Format ( Strings . RuleSuppressionRuleNameNotFound , _ruleName ) ;
56+ }
57+ }
3858 }
3959
4060 /// <summary>
@@ -46,6 +66,24 @@ public string RuleSuppressionID
4666 set ;
4767 }
4868
69+ /// <summary>
70+ /// Scope of the rule suppression
71+ /// </summary>
72+ public string Scope
73+ {
74+ get ;
75+ set ;
76+ }
77+
78+ /// <summary>
79+ /// Target of the rule suppression
80+ /// </summary>
81+ public string Target
82+ {
83+ get ;
84+ set ;
85+ }
86+
4987 /// <summary>
5088 /// Returns error occurred in trying to parse the attribute
5189 /// </summary>
@@ -55,6 +93,18 @@ public string Error
5593 set ;
5694 }
5795
96+ private static HashSet < string > scopeSet ;
97+
98+ /// <summary>
99+ /// Initialize the scopeSet
100+ /// </summary>
101+ static RuleSuppression ( )
102+ {
103+ scopeSet = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
104+ scopeSet . Add ( "function" ) ;
105+ scopeSet . Add ( "class" ) ;
106+ }
107+
58108 /// <summary>
59109 /// Returns rule suppression from an attribute ast that has the type suppressmessageattribute
60110 /// </summary>
@@ -85,6 +135,14 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
85135 {
86136 switch ( count )
87137 {
138+ case 4 :
139+ Target = ( positionalArguments [ 3 ] as StringConstantExpressionAst ) . Value ;
140+ goto case 3 ;
141+
142+ case 3 :
143+ Scope = ( positionalArguments [ 2 ] as StringConstantExpressionAst ) . Value ;
144+ goto case 2 ;
145+
88146 case 2 :
89147 RuleSuppressionID = ( positionalArguments [ 1 ] as StringConstantExpressionAst ) . Value ;
90148 goto case 1 ;
@@ -134,13 +192,64 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
134192 RuleSuppressionID = ( name . Argument as StringConstantExpressionAst ) . Value ;
135193 goto default ;
136194
195+ case "scope" :
196+ if ( ! String . IsNullOrWhiteSpace ( Scope ) )
197+ {
198+ Error = String . Format ( Strings . NamedAndPositionalArgumentsConflictError , name ) ;
199+ }
200+
201+ Scope = ( name . Argument as StringConstantExpressionAst ) . Value ;
202+
203+ if ( ! scopeSet . Contains ( Scope ) )
204+ {
205+ Error = Strings . WrongScopeArgumentSuppressionAttributeError ;
206+ }
207+
208+ goto default ;
209+
210+ case "target" :
211+ if ( ! String . IsNullOrWhiteSpace ( Target ) )
212+ {
213+ Error = String . Format ( Strings . NamedAndPositionalArgumentsConflictError , name ) ;
214+ }
215+
216+ Target = ( name . Argument as StringConstantExpressionAst ) . Value ;
217+ goto default ;
218+
137219 default :
138220 break ;
139221 }
140222 }
141223 }
224+
225+ // Must have scope and target together
226+ if ( String . IsNullOrWhiteSpace ( Scope ) && ! String . IsNullOrWhiteSpace ( Target ) )
227+ {
228+ Error = Strings . TargetWithoutScopeSuppressionAttributeError ;
229+ }
230+ }
231+
232+ StartOffset = start ;
233+ EndOffset = end ;
234+
235+ if ( ! String . IsNullOrWhiteSpace ( Error ) )
236+ {
237+ Error = String . Format ( CultureInfo . CurrentCulture , Strings . RuleSuppressionErrorFormat , attrAst . Extent . StartLineNumber ,
238+ System . IO . Path . GetFileName ( attrAst . Extent . File ) , Error ) ;
142239 }
240+ }
143241
242+ /// <summary>
243+ /// Constructs rule expression from rule name, id, start and end
244+ /// </summary>
245+ /// <param name="ruleName"></param>
246+ /// <param name="ruleSuppressionID"></param>
247+ /// <param name="start"></param>
248+ /// <param name="end"></param>
249+ public RuleSuppression ( string ruleName , string ruleSuppressionID , int start , int end )
250+ {
251+ RuleName = ruleName ;
252+ RuleSuppressionID = ruleSuppressionID ;
144253 StartOffset = start ;
145254 EndOffset = end ;
146255 }
@@ -153,11 +262,11 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
153262 /// <param name="start"></param>
154263 /// <param name="end"></param>
155264 /// <returns></returns>
156- public static List < RuleSuppression > GetSuppressions ( IEnumerable < AttributeAst > attrAsts , int start , int end )
265+ public static List < RuleSuppression > GetSuppressions ( IEnumerable < AttributeAst > attrAsts , int start , int end , Ast scopeAst )
157266 {
158267 List < RuleSuppression > result = new List < RuleSuppression > ( ) ;
159268
160- if ( attrAsts == null )
269+ if ( attrAsts == null || scopeAst == null )
161270 {
162271 return result ;
163272 }
@@ -169,8 +278,44 @@ public static List<RuleSuppression> GetSuppressions(IEnumerable<AttributeAst> at
169278 {
170279 RuleSuppression ruleSupp = new RuleSuppression ( attributeAst , start , end ) ;
171280
172- if ( string . IsNullOrWhiteSpace ( ruleSupp . Error ) )
281+ // If there is no error and scope is not null
282+ if ( String . IsNullOrWhiteSpace ( ruleSupp . Error ) && ! String . IsNullOrWhiteSpace ( ruleSupp . Scope ) )
283+ {
284+ if ( String . IsNullOrWhiteSpace ( ruleSupp . Target ) )
285+ {
286+ ruleSupp . Target = "*" ;
287+ }
288+
289+ // regex for wild card *
290+ Regex reg = new Regex ( String . Format ( "^{0}$" , Regex . Escape ( ruleSupp . Target ) . Replace ( @"\*" , ".*" ) ) ) ;
291+ IEnumerable < Ast > targetAsts = null ;
292+
293+ switch ( ruleSupp . Scope . ToLower ( ) )
294+ {
295+ case "function" :
296+ targetAsts = scopeAst . FindAll ( item => item is FunctionDefinitionAst && reg . IsMatch ( ( item as FunctionDefinitionAst ) . Name ) , true ) ;
297+ goto default ;
298+
299+ case "class" :
300+ targetAsts = scopeAst . FindAll ( item => item is TypeDefinitionAst && reg . IsMatch ( ( item as TypeDefinitionAst ) . Name ) , true ) ;
301+ goto default ;
302+
303+ default :
304+ break ;
305+ }
306+
307+ if ( targetAsts != null )
308+ {
309+ foreach ( Ast targetAst in targetAsts )
310+ {
311+ result . Add ( new RuleSuppression ( ruleSupp . RuleName , ruleSupp . RuleSuppressionID , targetAst . Extent . StartOffset , targetAst . Extent . EndOffset ) ) ;
312+ }
313+ }
314+
315+ }
316+ else
173317 {
318+ // this may add rule suppression that contains erro but we will check for this in the engine to throw out error
174319 result . Add ( ruleSupp ) ;
175320 }
176321 }
0 commit comments