Skip to content

Commit 33fad88

Browse files
committed
Also added 'IgnoredModules' parameter and updated tests and docs accordingly
1 parent 971f02b commit 33fad88

File tree

3 files changed

+474
-28
lines changed

3 files changed

+474
-28
lines changed

Rules/UseFullyQualifiedCmdletNames.cs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,27 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
4242
#if !CORECLR
4343
[Export(typeof(IScriptRule))]
4444
#endif
45-
public class UseFullyQualifiedCmdletNames : IScriptRule
45+
public class UseFullyQualifiedCmdletNames : ConfigurableRule
4646
{
4747
private ConcurrentDictionary<string, string> resolutionCache = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
4848

4949
internal const string AnalyzerName = "Microsoft.Windows.PowerShell.ScriptAnalyzer";
5050

51+
/// <summary>
52+
/// Modules to ignore when applying this rule.
53+
/// Commands from these modules will not be expanded to their fully qualified names.
54+
/// Default is empty array (no modules ignored - all cmdlets are processed).
55+
/// </summary>
56+
[ConfigurableRuleProperty(defaultValue: new string[] { })]
57+
public string[] IgnoredModules { get; protected set; }
58+
5159
/// <summary>
5260
/// Analyzes the given ast to find cmdlet invocations that are not fully qualified.
5361
/// </summary>
5462
/// <param name="ast">The script's ast</param>
5563
/// <param name="fileName">The script's file name</param>
5664
/// <returns>The diagnostic results of this rule</returns>
57-
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
65+
public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
5866
{
5967
if (ast == null)
6068
{
@@ -76,13 +84,17 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
7684
var resolvedCommand = ResolveCommand(commandName);
7785
if (resolvedCommand == null)
7886
{
87+
// Cache null results to avoid repeated lookups
88+
resolutionCache[commandName] = null;
7989
continue;
8090
}
8191

8292
if (resolvedCommand.CommandType != CommandTypes.Cmdlet &&
8393
resolvedCommand.CommandType != CommandTypes.Function &&
8494
resolvedCommand.CommandType != CommandTypes.Alias)
8595
{
96+
// Cache null results for non-cmdlet/function/alias commands
97+
resolutionCache[commandName] = null;
8698
continue;
8799
}
88100

@@ -93,6 +105,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
93105
{
94106
if (aliasInfo.ResolvedCommand == null)
95107
{
108+
resolutionCache[commandName] = null;
96109
continue;
97110
}
98111

@@ -102,12 +115,36 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
102115

103116
if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(actualCmdletName))
104117
{
118+
resolutionCache[commandName] = null;
119+
continue;
120+
}
121+
122+
// Check if the module is in the ignored list
123+
if (IgnoredModules != null && IgnoredModules.Contains(moduleName, StringComparer.OrdinalIgnoreCase))
124+
{
125+
// Cache null for ignored modules to avoid re-checking
126+
resolutionCache[commandName] = null;
105127
continue;
106128
}
107129

108130
fullyQualifiedName = $"{moduleName}\\{actualCmdletName}";
109131
resolutionCache[commandName] = fullyQualifiedName;
110132
}
133+
else
134+
{
135+
// If we have a cached result but it's null/empty, it means we should skip this command
136+
if (string.IsNullOrEmpty(fullyQualifiedName))
137+
{
138+
continue;
139+
}
140+
141+
// Re-check ignored modules for cached results (in case IgnoredModules was changed)
142+
var moduleName = fullyQualifiedName.Split('\\')[0];
143+
if (IgnoredModules != null && IgnoredModules.Contains(moduleName, StringComparer.OrdinalIgnoreCase))
144+
{
145+
continue;
146+
}
147+
}
111148

112149
var extent = commandAst.CommandElements[0].Extent;
113150

@@ -140,7 +177,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
140177
message,
141178
extent,
142179
GetName(),
143-
(DiagnosticSeverity)GetSeverity(),
180+
DiagnosticSeverity.Warning,
144181
fileName,
145182
null,
146183
suggestedCorrections);
@@ -161,7 +198,7 @@ private CommandInfo ResolveCommand(string commandName)
161198
/// Retrieves the localized name of this rule.
162199
/// </summary>
163200
/// <returns>The localized name of this rule</returns>
164-
public string GetName()
201+
public override string GetName()
165202
{
166203
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.UseFullyQualifiedCmdletNamesName);
167204
}
@@ -170,7 +207,7 @@ public string GetName()
170207
/// Retrieves the common name of this rule.
171208
/// </summary>
172209
/// <returns>The common name of this rule</returns>
173-
public string GetCommonName()
210+
public override string GetCommonName()
174211
{
175212
return string.Format(CultureInfo.CurrentCulture, Strings.UseFullyQualifiedCmdletNamesCommonName);
176213
}
@@ -179,7 +216,7 @@ public string GetCommonName()
179216
/// Retrieves the localized description of this rule.
180217
/// </summary>
181218
/// <returns>The localized description of this rule</returns>
182-
public string GetDescription()
219+
public override string GetDescription()
183220
{
184221
return string.Format(CultureInfo.CurrentCulture, Strings.UseFullyQualifiedCmdletNamesDescription);
185222
}
@@ -188,7 +225,7 @@ public string GetDescription()
188225
/// Retrieves the source type of this rule.
189226
/// </summary>
190227
/// <returns>The source type of this rule</returns>
191-
public SourceType GetSourceType()
228+
public override SourceType GetSourceType()
192229
{
193230
return SourceType.Builtin;
194231
}
@@ -197,7 +234,7 @@ public SourceType GetSourceType()
197234
/// Retrieves the source name of this rule.
198235
/// </summary>
199236
/// <returns>The source name of this rule</returns>
200-
public string GetSourceName()
237+
public override string GetSourceName()
201238
{
202239
return "PS";
203240
}
@@ -206,7 +243,7 @@ public string GetSourceName()
206243
/// Retrieves the severity of this rule.
207244
/// </summary>
208245
/// <returns>The severity of this rule</returns>
209-
public RuleSeverity GetSeverity()
246+
public override RuleSeverity GetSeverity()
210247
{
211248
return RuleSeverity.Warning;
212249
}

0 commit comments

Comments
 (0)