Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Engine/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ public bool IsDscResourceModule(string filePath)
return false;
}

/// <summary>
/// Given a filePath. Returns true if it is a powershell help file
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public bool IsHelpFile(string filePath)
{
return filePath != null && File.Exists(filePath) && Path.GetFileName(filePath).StartsWith("about_", StringComparison.OrdinalIgnoreCase)
&& Path.GetFileName(filePath).EndsWith(".help.txt", StringComparison.OrdinalIgnoreCase);
}

/// <summary>
/// Given an AST, checks whether dsc resource is class based or not
/// </summary>
Expand Down
121 changes: 74 additions & 47 deletions Engine/ScriptAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -908,9 +908,9 @@ private void BuildScriptPathList(
bool searchRecursively,
IList<string> scriptFilePaths)
{
const string ps1Suffix = "ps1";
const string psm1Suffix = "psm1";
const string psd1Suffix = "psd1";
const string ps1Suffix = ".ps1";
const string psm1Suffix = ".psm1";
const string psd1Suffix = ".psd1";

if (Directory.Exists(path))
{
Expand All @@ -935,9 +935,14 @@ private void BuildScriptPathList(
}
else if (File.Exists(path))
{
if ((path.Length > ps1Suffix.Length && path.Substring(path.Length - ps1Suffix.Length).Equals(ps1Suffix, StringComparison.OrdinalIgnoreCase)) ||
(path.Length > psm1Suffix.Length && path.Substring(path.Length - psm1Suffix.Length).Equals(psm1Suffix, StringComparison.OrdinalIgnoreCase)) ||
(path.Length > psd1Suffix.Length && path.Substring(path.Length - psd1Suffix.Length).Equals(psd1Suffix, StringComparison.OrdinalIgnoreCase)))
String fileName = Path.GetFileName(path);
if ((fileName.Length > ps1Suffix.Length && String.Equals(Path.GetExtension(path), ps1Suffix, StringComparison.OrdinalIgnoreCase)) ||
(fileName.Length > psm1Suffix.Length && String.Equals(Path.GetExtension(path), psm1Suffix, StringComparison.OrdinalIgnoreCase)) ||
(fileName.Length > psd1Suffix.Length && String.Equals(Path.GetExtension(path), psd1Suffix, StringComparison.OrdinalIgnoreCase)))
{
scriptFilePaths.Add(path);
}
else if (Helper.Instance.IsHelpFile(path))
{
scriptFilePaths.Add(path);
}
Expand All @@ -964,7 +969,28 @@ private IEnumerable<DiagnosticRecord> AnalyzeFile(string filePath)
//Parse the file
if (File.Exists(filePath))
{
scriptAst = Parser.ParseFile(filePath, out scriptTokens, out errors);
// processing for non help script
if (!(Path.GetFileName(filePath).StartsWith("about_") && Path.GetFileName(filePath).EndsWith(".help.txt")))
{
scriptAst = Parser.ParseFile(filePath, out scriptTokens, out errors);

if (errors != null && errors.Length > 0)
{
foreach (ParseError error in errors)
{
string parseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorFormat, error.Extent.File, error.Message.TrimEnd('.'), error.Extent.StartLineNumber, error.Extent.StartColumnNumber);
this.outputWriter.WriteError(new ErrorRecord(new ParseException(parseErrorMessage), parseErrorMessage, ErrorCategory.ParserError, error.ErrorId));
}
}

if (errors.Length > 10)
{
string manyParseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorMessage, System.IO.Path.GetFileName(filePath));
this.outputWriter.WriteError(new ErrorRecord(new ParseException(manyParseErrorMessage), manyParseErrorMessage, ErrorCategory.ParserError, filePath));

return new List<DiagnosticRecord>();
}
}
}
else
{
Expand All @@ -975,23 +1001,6 @@ private IEnumerable<DiagnosticRecord> AnalyzeFile(string filePath)
return null;
}

if (errors != null && errors.Length > 0)
{
foreach (ParseError error in errors)
{
string parseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorFormat, error.Extent.File, error.Message.TrimEnd('.'), error.Extent.StartLineNumber, error.Extent.StartColumnNumber);
this.outputWriter.WriteError(new ErrorRecord(new ParseException(parseErrorMessage), parseErrorMessage, ErrorCategory.ParserError, error.ErrorId));
}
}

if (errors.Length > 10)
{
string manyParseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorMessage, System.IO.Path.GetFileName(filePath));
this.outputWriter.WriteError(new ErrorRecord(new ParseException(manyParseErrorMessage), manyParseErrorMessage, ErrorCategory.ParserError, filePath));

return new List<DiagnosticRecord>();
}

return this.AnalyzeSyntaxTree(scriptAst, scriptTokens, filePath);
}

Expand All @@ -1007,36 +1016,41 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
Token[] scriptTokens,
string filePath)
{
Dictionary<string, List<RuleSuppression>> ruleSuppressions;
Dictionary<string, List<RuleSuppression>> ruleSuppressions = new Dictionary<string,List<RuleSuppression>>();
ConcurrentBag<DiagnosticRecord> diagnostics = new ConcurrentBag<DiagnosticRecord>();
ConcurrentBag<SuppressedRecord> suppressed = new ConcurrentBag<SuppressedRecord>();
BlockingCollection<List<object>> verboseOrErrors = new BlockingCollection<List<object>>();

// Use a List of KVP rather than dictionary, since for a script containing inline functions with same signature, keys clash
List<KeyValuePair<CommandInfo, IScriptExtent>> cmdInfoTable = new List<KeyValuePair<CommandInfo, IScriptExtent>>();

ruleSuppressions = Helper.Instance.GetRuleSuppression(scriptAst);
bool helpFile = (scriptAst == null) && Helper.Instance.IsHelpFile(filePath);

foreach (List<RuleSuppression> ruleSuppressionsList in ruleSuppressions.Values)
if (!helpFile)
{
foreach (RuleSuppression ruleSuppression in ruleSuppressionsList)
ruleSuppressions = Helper.Instance.GetRuleSuppression(scriptAst);

foreach (List<RuleSuppression> ruleSuppressionsList in ruleSuppressions.Values)
{
if (!String.IsNullOrWhiteSpace(ruleSuppression.Error))
foreach (RuleSuppression ruleSuppression in ruleSuppressionsList)
{
this.outputWriter.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression));
if (!String.IsNullOrWhiteSpace(ruleSuppression.Error))
{
this.outputWriter.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression));
}
}
}
}

#region Run VariableAnalysis
try
{
Helper.Instance.InitializeVariableAnalysis(scriptAst);
}
catch { }
#endregion
#region Run VariableAnalysis
try
{
Helper.Instance.InitializeVariableAnalysis(scriptAst);
}
catch { }
#endregion

Helper.Instance.Tokens = scriptTokens;
Helper.Instance.Tokens = scriptTokens;
}

#region Run ScriptRules
//Trim down to the leaf element of the filePath and pass it to Diagnostic Record
Expand Down Expand Up @@ -1067,6 +1081,8 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
}
}

bool helpRule = String.Equals(scriptRule.GetName(), "PSUseUTF8EncodingForHelpFile", StringComparison.OrdinalIgnoreCase);

if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
{
List<object> result = new List<object>();
Expand All @@ -1077,14 +1093,25 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
// We want the Engine to continue functioning even if one or more Rules throws an exception
try
{
var records = Helper.Instance.SuppressRule(scriptRule.GetName(), ruleSuppressions, scriptRule.AnalyzeScript(scriptAst, scriptAst.Extent.File).ToList());
foreach (var record in records.Item2)
if (helpRule && helpFile)
{
diagnostics.Add(record);
var records = scriptRule.AnalyzeScript(scriptAst, filePath);
foreach (var record in records)
{
diagnostics.Add(record);
}
}
foreach (var suppressedRec in records.Item1)
else if (!helpRule && !helpFile)
{
suppressed.Add(suppressedRec);
var records = Helper.Instance.SuppressRule(scriptRule.GetName(), ruleSuppressions, scriptRule.AnalyzeScript(scriptAst, scriptAst.Extent.File).ToList());
foreach (var record in records.Item2)
{
diagnostics.Add(record);
}
foreach (var suppressedRec in records.Item1)
{
suppressed.Add(suppressedRec);
}
}
}
catch (Exception scriptRuleException)
Expand Down Expand Up @@ -1122,7 +1149,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(

#region Run Token Rules

if (this.TokenRules != null)
if (this.TokenRules != null && !helpFile)
{
foreach (ITokenRule tokenRule in this.TokenRules)
{
Expand Down Expand Up @@ -1173,7 +1200,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
#endregion

#region DSC Resource Rules
if (this.DSCResourceRules != null)
if (this.DSCResourceRules != null && !helpFile)
{
// Invoke AnalyzeDSCClass only if the ast is a class based resource
if (Helper.Instance.IsDscResourceClassBased(scriptAst))
Expand Down Expand Up @@ -1282,7 +1309,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(

#region Run External Rules

if (this.ExternalRules != null)
if (this.ExternalRules != null && !helpFile)
{
List<ExternalRule> exRules = new List<ExternalRule>();

Expand Down
1 change: 1 addition & 0 deletions Rules/ScriptAnalyzerBuiltinRules.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
<Compile Include="UseShouldProcessForStateChangingFunctions.cs" />
<Compile Include="UseSingularNouns.cs" />
<Compile Include="UseStandardDSCFunctionsInResource.cs" />
<Compile Include="UseUTF8EncodingForHelpFile.cs" />
<Compile Include="ReturnCorrectTypesForDSCFunctions.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
38 changes: 37 additions & 1 deletion Rules/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Rules/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -732,4 +732,16 @@
<data name="AvoidUsingDeprecatedManifestFieldsName" xml:space="preserve">
<value>AvoidUsingDeprecatedManifestFields</value>
</data>
<data name="UseUTF8EncodingForHelpFileCommonName" xml:space="preserve">
<value>Use UTF8 Encoding For Help File</value>
</data>
<data name="UseUTF8EncodingForHelpFileDescription" xml:space="preserve">
<value>PowerShell help file needs to use UTF8 Encoding.</value>
</data>
<data name="UseUTF8EncodingForHelpFileError" xml:space="preserve">
<value>File {0} has to use UTF8 instead of {1} encoding because it is a powershell help file.</value>
</data>
<data name="UseUTF8EncodingForHelpFileName" xml:space="preserve">
<value>UseUTF8EncodingForHelpFile</value>
</data>
</root>
Loading