Skip to content

Commit 61b8454

Browse files
committed
Merge pull request #298 from PowerShell/development
Dev to Master
2 parents 8cbf460 + 0b0626e commit 61b8454

32 files changed

+306
-56
lines changed

Engine/Helper.cs

+11
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,17 @@ public bool IsDscResourceModule(string filePath)
224224
return false;
225225
}
226226

227+
/// <summary>
228+
/// Given a filePath. Returns true if it is a powershell help file
229+
/// </summary>
230+
/// <param name="filePath"></param>
231+
/// <returns></returns>
232+
public bool IsHelpFile(string filePath)
233+
{
234+
return filePath != null && File.Exists(filePath) && Path.GetFileName(filePath).StartsWith("about_", StringComparison.OrdinalIgnoreCase)
235+
&& Path.GetFileName(filePath).EndsWith(".help.txt", StringComparison.OrdinalIgnoreCase);
236+
}
237+
227238
/// <summary>
228239
/// Given an AST, checks whether dsc resource is class based or not
229240
/// </summary>

Engine/ScriptAnalyzer.cs

+74-47
Original file line numberDiff line numberDiff line change
@@ -908,9 +908,9 @@ private void BuildScriptPathList(
908908
bool searchRecursively,
909909
IList<string> scriptFilePaths)
910910
{
911-
const string ps1Suffix = "ps1";
912-
const string psm1Suffix = "psm1";
913-
const string psd1Suffix = "psd1";
911+
const string ps1Suffix = ".ps1";
912+
const string psm1Suffix = ".psm1";
913+
const string psd1Suffix = ".psd1";
914914

915915
if (Directory.Exists(path))
916916
{
@@ -935,9 +935,14 @@ private void BuildScriptPathList(
935935
}
936936
else if (File.Exists(path))
937937
{
938-
if ((path.Length > ps1Suffix.Length && path.Substring(path.Length - ps1Suffix.Length).Equals(ps1Suffix, StringComparison.OrdinalIgnoreCase)) ||
939-
(path.Length > psm1Suffix.Length && path.Substring(path.Length - psm1Suffix.Length).Equals(psm1Suffix, StringComparison.OrdinalIgnoreCase)) ||
940-
(path.Length > psd1Suffix.Length && path.Substring(path.Length - psd1Suffix.Length).Equals(psd1Suffix, StringComparison.OrdinalIgnoreCase)))
938+
String fileName = Path.GetFileName(path);
939+
if ((fileName.Length > ps1Suffix.Length && String.Equals(Path.GetExtension(path), ps1Suffix, StringComparison.OrdinalIgnoreCase)) ||
940+
(fileName.Length > psm1Suffix.Length && String.Equals(Path.GetExtension(path), psm1Suffix, StringComparison.OrdinalIgnoreCase)) ||
941+
(fileName.Length > psd1Suffix.Length && String.Equals(Path.GetExtension(path), psd1Suffix, StringComparison.OrdinalIgnoreCase)))
942+
{
943+
scriptFilePaths.Add(path);
944+
}
945+
else if (Helper.Instance.IsHelpFile(path))
941946
{
942947
scriptFilePaths.Add(path);
943948
}
@@ -964,7 +969,28 @@ private IEnumerable<DiagnosticRecord> AnalyzeFile(string filePath)
964969
//Parse the file
965970
if (File.Exists(filePath))
966971
{
967-
scriptAst = Parser.ParseFile(filePath, out scriptTokens, out errors);
972+
// processing for non help script
973+
if (!(Path.GetFileName(filePath).StartsWith("about_") && Path.GetFileName(filePath).EndsWith(".help.txt")))
974+
{
975+
scriptAst = Parser.ParseFile(filePath, out scriptTokens, out errors);
976+
977+
if (errors != null && errors.Length > 0)
978+
{
979+
foreach (ParseError error in errors)
980+
{
981+
string parseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorFormat, error.Extent.File, error.Message.TrimEnd('.'), error.Extent.StartLineNumber, error.Extent.StartColumnNumber);
982+
this.outputWriter.WriteError(new ErrorRecord(new ParseException(parseErrorMessage), parseErrorMessage, ErrorCategory.ParserError, error.ErrorId));
983+
}
984+
}
985+
986+
if (errors.Length > 10)
987+
{
988+
string manyParseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorMessage, System.IO.Path.GetFileName(filePath));
989+
this.outputWriter.WriteError(new ErrorRecord(new ParseException(manyParseErrorMessage), manyParseErrorMessage, ErrorCategory.ParserError, filePath));
990+
991+
return new List<DiagnosticRecord>();
992+
}
993+
}
968994
}
969995
else
970996
{
@@ -975,23 +1001,6 @@ private IEnumerable<DiagnosticRecord> AnalyzeFile(string filePath)
9751001
return null;
9761002
}
9771003

978-
if (errors != null && errors.Length > 0)
979-
{
980-
foreach (ParseError error in errors)
981-
{
982-
string parseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorFormat, error.Extent.File, error.Message.TrimEnd('.'), error.Extent.StartLineNumber, error.Extent.StartColumnNumber);
983-
this.outputWriter.WriteError(new ErrorRecord(new ParseException(parseErrorMessage), parseErrorMessage, ErrorCategory.ParserError, error.ErrorId));
984-
}
985-
}
986-
987-
if (errors.Length > 10)
988-
{
989-
string manyParseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorMessage, System.IO.Path.GetFileName(filePath));
990-
this.outputWriter.WriteError(new ErrorRecord(new ParseException(manyParseErrorMessage), manyParseErrorMessage, ErrorCategory.ParserError, filePath));
991-
992-
return new List<DiagnosticRecord>();
993-
}
994-
9951004
return this.AnalyzeSyntaxTree(scriptAst, scriptTokens, filePath);
9961005
}
9971006

@@ -1007,36 +1016,41 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
10071016
Token[] scriptTokens,
10081017
string filePath)
10091018
{
1010-
Dictionary<string, List<RuleSuppression>> ruleSuppressions;
1019+
Dictionary<string, List<RuleSuppression>> ruleSuppressions = new Dictionary<string,List<RuleSuppression>>();
10111020
ConcurrentBag<DiagnosticRecord> diagnostics = new ConcurrentBag<DiagnosticRecord>();
10121021
ConcurrentBag<SuppressedRecord> suppressed = new ConcurrentBag<SuppressedRecord>();
10131022
BlockingCollection<List<object>> verboseOrErrors = new BlockingCollection<List<object>>();
10141023

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

1018-
ruleSuppressions = Helper.Instance.GetRuleSuppression(scriptAst);
1027+
bool helpFile = (scriptAst == null) && Helper.Instance.IsHelpFile(filePath);
10191028

1020-
foreach (List<RuleSuppression> ruleSuppressionsList in ruleSuppressions.Values)
1029+
if (!helpFile)
10211030
{
1022-
foreach (RuleSuppression ruleSuppression in ruleSuppressionsList)
1031+
ruleSuppressions = Helper.Instance.GetRuleSuppression(scriptAst);
1032+
1033+
foreach (List<RuleSuppression> ruleSuppressionsList in ruleSuppressions.Values)
10231034
{
1024-
if (!String.IsNullOrWhiteSpace(ruleSuppression.Error))
1035+
foreach (RuleSuppression ruleSuppression in ruleSuppressionsList)
10251036
{
1026-
this.outputWriter.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression));
1037+
if (!String.IsNullOrWhiteSpace(ruleSuppression.Error))
1038+
{
1039+
this.outputWriter.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression));
1040+
}
10271041
}
10281042
}
1029-
}
10301043

1031-
#region Run VariableAnalysis
1032-
try
1033-
{
1034-
Helper.Instance.InitializeVariableAnalysis(scriptAst);
1035-
}
1036-
catch { }
1037-
#endregion
1044+
#region Run VariableAnalysis
1045+
try
1046+
{
1047+
Helper.Instance.InitializeVariableAnalysis(scriptAst);
1048+
}
1049+
catch { }
1050+
#endregion
10381051

1039-
Helper.Instance.Tokens = scriptTokens;
1052+
Helper.Instance.Tokens = scriptTokens;
1053+
}
10401054

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

1084+
bool helpRule = String.Equals(scriptRule.GetName(), "PSUseUTF8EncodingForHelpFile", StringComparison.OrdinalIgnoreCase);
1085+
10701086
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
10711087
{
10721088
List<object> result = new List<object>();
@@ -1077,14 +1093,25 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
10771093
// We want the Engine to continue functioning even if one or more Rules throws an exception
10781094
try
10791095
{
1080-
var records = Helper.Instance.SuppressRule(scriptRule.GetName(), ruleSuppressions, scriptRule.AnalyzeScript(scriptAst, scriptAst.Extent.File).ToList());
1081-
foreach (var record in records.Item2)
1096+
if (helpRule && helpFile)
10821097
{
1083-
diagnostics.Add(record);
1098+
var records = scriptRule.AnalyzeScript(scriptAst, filePath);
1099+
foreach (var record in records)
1100+
{
1101+
diagnostics.Add(record);
1102+
}
10841103
}
1085-
foreach (var suppressedRec in records.Item1)
1104+
else if (!helpRule && !helpFile)
10861105
{
1087-
suppressed.Add(suppressedRec);
1106+
var records = Helper.Instance.SuppressRule(scriptRule.GetName(), ruleSuppressions, scriptRule.AnalyzeScript(scriptAst, scriptAst.Extent.File).ToList());
1107+
foreach (var record in records.Item2)
1108+
{
1109+
diagnostics.Add(record);
1110+
}
1111+
foreach (var suppressedRec in records.Item1)
1112+
{
1113+
suppressed.Add(suppressedRec);
1114+
}
10881115
}
10891116
}
10901117
catch (Exception scriptRuleException)
@@ -1122,7 +1149,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
11221149

11231150
#region Run Token Rules
11241151

1125-
if (this.TokenRules != null)
1152+
if (this.TokenRules != null && !helpFile)
11261153
{
11271154
foreach (ITokenRule tokenRule in this.TokenRules)
11281155
{
@@ -1173,7 +1200,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
11731200
#endregion
11741201

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

12831310
#region Run External Rules
12841311

1285-
if (this.ExternalRules != null)
1312+
if (this.ExternalRules != null && !helpFile)
12861313
{
12871314
List<ExternalRule> exRules = new List<ExternalRule>();
12881315

Rules/ScriptAnalyzerBuiltinRules.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
<Compile Include="AvoidShouldContinueWithoutForce.cs" />
6161
<Compile Include="AvoidUsingDeprecatedManifestFields.cs" />
6262
<Compile Include="ProvideDefaultParameterValue.cs" />
63-
<Compile Include="AvoidUninitializedVariable.cs" />
6463
<Compile Include="AvoidUsernameAndPasswordParams.cs" />
6564
<Compile Include="AvoidUsingComputerNameHardcoded.cs" />
6665
<Compile Include="AvoidUsingConvertToSecureStringWithPlainText.cs" />
@@ -91,6 +90,7 @@
9190
<Compile Include="UseShouldProcessForStateChangingFunctions.cs" />
9291
<Compile Include="UseSingularNouns.cs" />
9392
<Compile Include="UseStandardDSCFunctionsInResource.cs" />
93+
<Compile Include="UseUTF8EncodingForHelpFile.cs" />
9494
<Compile Include="ReturnCorrectTypesForDSCFunctions.cs" />
9595
</ItemGroup>
9696
<ItemGroup>

Rules/Strings.Designer.cs

+37-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rules/Strings.resx

+12
Original file line numberDiff line numberDiff line change
@@ -732,4 +732,16 @@
732732
<data name="AvoidUsingDeprecatedManifestFieldsName" xml:space="preserve">
733733
<value>AvoidUsingDeprecatedManifestFields</value>
734734
</data>
735+
<data name="UseUTF8EncodingForHelpFileCommonName" xml:space="preserve">
736+
<value>Use UTF8 Encoding For Help File</value>
737+
</data>
738+
<data name="UseUTF8EncodingForHelpFileDescription" xml:space="preserve">
739+
<value>PowerShell help file needs to use UTF8 Encoding.</value>
740+
</data>
741+
<data name="UseUTF8EncodingForHelpFileError" xml:space="preserve">
742+
<value>File {0} has to use UTF8 instead of {1} encoding because it is a powershell help file.</value>
743+
</data>
744+
<data name="UseUTF8EncodingForHelpFileName" xml:space="preserve">
745+
<value>UseUTF8EncodingForHelpFile</value>
746+
</data>
735747
</root>

0 commit comments

Comments
 (0)