From f8f808370aaa07a7309e5e7cc9cb7cb9a6b4b3a1 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 17 Jun 2019 21:41:20 -0500 Subject: [PATCH 01/75] Annotatively research current state of exception handling surrounding settings file load invocation --- .../Commands/InvokeScriptAnalyzerCommand.cs | 12 +++++++++++ Engine/Helper.cs | 6 ++++++ Engine/Settings.cs | 21 ++++++++++++++++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs index c51fc9f38..af9b06386 100644 --- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs +++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs @@ -268,6 +268,7 @@ protected override void BeginProcessing() Helper.Instance = new Helper( SessionState.InvokeCommand, this); + // NOTE Helper.Instance.Initialize() does *not* modify this.settings. Helper.Instance.Initialize(); var psVersionTable = this.SessionState.PSVariable.GetValue("PSVersionTable") as Hashtable; @@ -276,6 +277,7 @@ protected override void BeginProcessing() Helper.Instance.SetPSVersionTable(psVersionTable); } + // NOTE Helper.ProcessCustomRulePaths does *not* modify this.settings. string[] rulePaths = Helper.ProcessCustomRulePaths( customRulePath, this.SessionState, @@ -284,6 +286,7 @@ protected override void BeginProcessing() if (IsFileParameterSet() && Path != null) { // just used to obtain the directory to use to find settings below + // NOTE ProcessPath() does *not* modify this.settings. ProcessPath(); } @@ -292,17 +295,24 @@ protected override void BeginProcessing() var combIncludeDefaultRules = IncludeDefaultRules.IsPresent; try { + // THROW PSSASettings.Create(object, string, IOutputWriter, GetResolvedProviderPathFromPSPath) throws if TODO + // NOTE Microsoft.Windows.PowerShell.ScriptAnalyzer.Settings.Create(...) types the input, gets the input (if necessary), and parses it (if any). var settingsObj = PSSASettings.Create( + // NOTHROW A null this.settings results in returning an "empty" (but not null) settingsObj without exception. + // NOTE this.settings is unmodified since start of BeginProcessing(). Thus, it is exactly the raw argument value, if any. settings, processedPaths == null || processedPaths.Count == 0 ? CurrentProviderLocation("FileSystem").ProviderPath : processedPaths[0], this, GetResolvedProviderPathFromPSPath); + // NOTE settingsObj cannot be null here since PSSASettings.Create(...) returns exactly `new Settings(settingsFound)`, which can never be null (but can throw). if (settingsObj != null) { + // NOTHROW UpdateSettings can throw an ArgumentNullException, but that will never happen since settingsObj is tested for non-nullity immediately above. ScriptAnalyzer.Instance.UpdateSettings(settingsObj); // For includeDefaultRules and RecurseCustomRulePath we override the value in the settings file by // command line argument. + // NOTHROW InvokeScriptAnalyzerCommand.OverrideSwitchParam(bool, string) combRecurseCustomRulePath = OverrideSwitchParam( settingsObj.RecurseCustomRulePath, "RecurseCustomRulePath"); @@ -315,6 +325,7 @@ protected override void BeginProcessing() // simultaneously. But since, this was done before with IncludeRules, ExcludeRules and Severity, // we use the same strategy for CustomRulePath. So, we take the union of CustomRulePath provided in // the settings file and if provided on command line. + // THROW Helper.ProcessCustomRulePaths(string[], SessionState, bool) throws one of six different exceptions if a settings' custom rule path is invalid somehow (e.g. drive doesn't exit; no wildcards but item doesn't exist; provider throws a lower-level exception; etc.). See the implementation of Helper.ProcessCustomRulePaths(string[], SessionState, bool) for details. var settingsCustomRulePath = Helper.ProcessCustomRulePaths( settingsObj?.CustomRulePath?.ToArray(), this.SessionState, @@ -327,6 +338,7 @@ protected override void BeginProcessing() } catch { + // NOTE Any exception in resolving, getting, parsing, updating, etc. the settings herein results in an contextless WriteWarning(Strings.SettingsNotParsable), regardless of provenance. this.WriteWarning(String.Format(CultureInfo.CurrentCulture, Strings.SettingsNotParsable)); stopProcessing = true; return; diff --git a/Engine/Helper.cs b/Engine/Helper.cs index 330547d02..e504868d4 100644 --- a/Engine/Helper.cs +++ b/Engine/Helper.cs @@ -1506,6 +1506,12 @@ public static string[] ProcessCustomRulePaths(string[] rulePaths, SessionState s Collection pathInfo = new Collection(); foreach (string rulePath in rulePaths) { + // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.Management.Automation.ProviderNotFoundException] if path is a provider-qualified path and the specified provider does not exist. + // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.Management.Automation.DriveNotFoundException] if path is a drive-qualified path and the specified drive does not exist. + // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.Management.Automation.ProviderInvocationException] if the provider throws an exception when its MakePath gets called. + // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.NotSupportedException] if the provider does not support multiple items. + // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.InvalidOperationException] the home location for the provider is not set and path starts with a "~". + // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.Management.Automation.ItemNotFoundException] if path does not contain wildcard characters and could not be found. Collection pathInfosForRulePath = sessionState.Path.GetResolvedPSPathFromPSPath(rulePath); if (null != pathInfosForRulePath) { diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 506733cc2..74a357767 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -73,10 +73,12 @@ public Settings(object settings, Func presetResolver) if (File.Exists(settingsFilePath)) { filePath = settingsFilePath; + // QUESTION When does parseSettingsFile(string) throw? parseSettingsFile(settingsFilePath); } else { + // THROW ArgumentException(Strings.InvalidPath) if the resolution of the settings argument via GetResolvedProviderPathFromPSPathDelegate is non-null but not an existing file. (e.g. it may be a directory) throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, @@ -89,10 +91,12 @@ public Settings(object settings, Func presetResolver) var settingsHashtable = settings as Hashtable; if (settingsHashtable != null) { + // QUESTION When does parseSettingsHashtable(Hashtable) throw? parseSettingsHashtable(settingsHashtable); } else { + // THROW ArgumentException(Strings.SettingsInvalidType) if settings is not convertible (`as` keyword) to Hashtable. throw new ArgumentException(Strings.SettingsInvalidType); } } @@ -179,11 +183,12 @@ public static string GetSettingPresetFilePath(string settingPreset) /// An output writer. /// The GetResolvedProviderPathFromPSPath method from PSCmdlet to resolve relative path including wildcard support. /// An object of Settings type. + // THROW Settings.Create(object, string, IOutputWriter, GetResolvedProviderPathFromPSPath) throws only because of the contained invocation to the Settings constructor, which does throw. internal static Settings Create(object settingsObj, string cwd, IOutputWriter outputWriter, PathResolver.GetResolvedProviderPathFromPSPath> getResolvedProviderPathFromPSPathDelegate) { object settingsFound; - var settingsMode = FindSettingsMode(settingsObj, cwd, out settingsFound); + var settingsMode = FindSettingsMode(settingsObj, cwd, out settingsFound); // NOTHROW switch (settingsMode) { @@ -205,6 +210,7 @@ internal static Settings Create(object settingsObj, string cwd, IOutputWriter ou var userProvidedSettingsString = settingsFound.ToString(); try { + // THROW ..Single() throws [System.InvalidOperationException] if the settings path does not resolve to exactly one item. (more or less) var resolvedPath = getResolvedProviderPathFromPSPathDelegate(userProvidedSettingsString, out ProviderInfo providerInfo).Single(); settingsFound = resolvedPath; outputWriter?.WriteVerbose( @@ -213,8 +219,11 @@ internal static Settings Create(object settingsObj, string cwd, IOutputWriter ou Strings.SettingsUsingFile, resolvedPath)); } + // NOTE This catch block effectively makes *any* exception resulting from resolving the settings file path reduce to the case of no settings (by way of null settingsFound). Only a WriteVerbose(Strings.SettingsCannotFindFile) identifies what happened. catch { + // TODO Upgrade WriteVerbose(Strings.SettingsCannotFindFile) to WriteWarning(Strings.SettingsCannotFindFile). + // TODO Perform a ShouldContinue (?) confirmation check in the event that an exception occurred while resolving the settings file path. outputWriter?.WriteVerbose( String.Format( CultureInfo.CurrentCulture, @@ -238,6 +247,7 @@ internal static Settings Create(object settingsObj, string cwd, IOutputWriter ou return null; } + // THROW new Settings(object) return new Settings(settingsFound); } @@ -456,16 +466,19 @@ private void parseSettingsHashtable(Hashtable settingsHashtable) } } + // NOTE parseSettingsFile concludes by calling parseSettingsHashtable if it (parseSettingsFile) is successful. private void parseSettingsFile(string settingsFilePath) { Token[] parserTokens = null; ParseError[] parserErrors = null; + // QUESTION Can Parser.ParseFile throw? Ast profileAst = Parser.ParseFile(settingsFilePath, out parserTokens, out parserErrors); IEnumerable hashTableAsts = profileAst.FindAll(item => item is HashtableAst, false); // no hashtable, raise warning if (hashTableAsts.Count() == 0) { + // THROW parseSettingsFile throws ArgumentException(Strings.InvalidProfile) if no hashTableAst is detected in settings file. throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.InvalidProfile, settingsFilePath)); } @@ -475,15 +488,18 @@ private void parseSettingsFile(string settingsFilePath) { // ideally we should use HashtableAst.SafeGetValue() but since // it is not available on PSv3, we resort to our own narrow implementation. + // QUESTION When does GetSafeValueFromHashtableAst(hashTableAst) throw? hashtable = GetSafeValueFromHashtableAst(hashTableAst); } catch (InvalidOperationException e) { + // THROW parseSettingsFile throws ArgumentException(Strings.InvalidProfile) if GetSafeValueFromHashtableAst(hashTableAst) throws an InvalidOperationException. throw new ArgumentException(Strings.InvalidProfile, e); } if (hashtable == null) { + // THROW parseSettingsFile throws ArgumentException if GetSafeValueFromHashtableAst(hashTableAst) returns null. throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, @@ -491,6 +507,7 @@ private void parseSettingsFile(string settingsFilePath) settingsFilePath)); } + // QUESTION When does parseSettingsHashtable(Hashtable) throw? parseSettingsHashtable(hashtable); } @@ -686,6 +703,7 @@ private static bool IsBuiltinSettingPreset(object settingPreset) return false; } + // NOTHROW FindSettingsMode(object, string, out object) internal static SettingsMode FindSettingsMode(object settings, string path, out object settingsFound) { var settingsMode = SettingsMode.None; @@ -706,6 +724,7 @@ internal static SettingsMode FindSettingsMode(object settings, string path, out { // if settings are not provided explicitly, look for it in the given path // check if pssasettings.psd1 exists + // COMBAK Refactor the magic string literal "PSScriptAnalyzerSettings.psd1" to a public const field on the class. (bare minimum... although will also help open possibility for user-configurable default name) var settingsFilename = "PSScriptAnalyzerSettings.psd1"; var settingsFilePath = Path.Combine(directory, settingsFilename); settingsFound = settingsFilePath; From b75dced398b7255a5645923e0effc290cf0a2084 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Tue, 18 Jun 2019 05:05:14 -0500 Subject: [PATCH 02/75] Throw a contextful terminating error if processing the argument of 'Invoke-ScriptAnalyzer -Settings' results in an exception --- Engine/Commands/InvokeScriptAnalyzerCommand.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs index af9b06386..f34b856b1 100644 --- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs +++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs @@ -295,7 +295,7 @@ protected override void BeginProcessing() var combIncludeDefaultRules = IncludeDefaultRules.IsPresent; try { - // THROW PSSASettings.Create(object, string, IOutputWriter, GetResolvedProviderPathFromPSPath) throws if TODO + // THROW PSSASettings.Create(object, string, IOutputWriter, GetResolvedProviderPathFromPSPath) throws if ... // NOTE Microsoft.Windows.PowerShell.ScriptAnalyzer.Settings.Create(...) types the input, gets the input (if necessary), and parses it (if any). var settingsObj = PSSASettings.Create( // NOTHROW A null this.settings results in returning an "empty" (but not null) settingsObj without exception. @@ -307,7 +307,7 @@ protected override void BeginProcessing() // NOTE settingsObj cannot be null here since PSSASettings.Create(...) returns exactly `new Settings(settingsFound)`, which can never be null (but can throw). if (settingsObj != null) { - // NOTHROW UpdateSettings can throw an ArgumentNullException, but that will never happen since settingsObj is tested for non-nullity immediately above. + // NOTHROW UpdateSettings(object) can throw an ArgumentNullException, but that will never happen since settingsObj is tested for non-nullity immediately above. ScriptAnalyzer.Instance.UpdateSettings(settingsObj); // For includeDefaultRules and RecurseCustomRulePath we override the value in the settings file by @@ -336,12 +336,15 @@ protected override void BeginProcessing() ? rulePaths : rulePaths.Concat(settingsCustomRulePath).ToArray(); } - catch + catch (Exception e) { // NOTE Any exception in resolving, getting, parsing, updating, etc. the settings herein results in an contextless WriteWarning(Strings.SettingsNotParsable), regardless of provenance. - this.WriteWarning(String.Format(CultureInfo.CurrentCulture, Strings.SettingsNotParsable)); - stopProcessing = true; - return; + var errorRecord = new ErrorRecord( + e, + "SettingsInvalidOrNotFound,Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands.InvokeScriptAnalyzerCommand", + ErrorCategory.InvalidArgument, + settings); + this.ThrowTerminatingError(errorRecord); } ScriptAnalyzer.Instance.Initialize( @@ -369,7 +372,7 @@ protected override void ProcessRecord() { ProcessPath(); } - + #if !PSV3 // TODO Support dependency resolution for analyzing script definitions if (saveDscDependency) From 66483449be59d4f1524c210cee280f4ebd81ea19 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Tue, 18 Jun 2019 06:33:55 -0500 Subject: [PATCH 03/75] Refine id and category of error for when processing value of `Invoke-ScriptAnalyzer -Settings` results in an exception --- .../Commands/InvokeScriptAnalyzerCommand.cs | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs index f34b856b1..6a12d1c13 100644 --- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs +++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs @@ -8,6 +8,7 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.IO; using System.Linq; using System.Management.Automation; using System.Management.Automation.Runspaces; @@ -339,11 +340,47 @@ protected override void BeginProcessing() catch (Exception e) { // NOTE Any exception in resolving, getting, parsing, updating, etc. the settings herein results in an contextless WriteWarning(Strings.SettingsNotParsable), regardless of provenance. - var errorRecord = new ErrorRecord( - e, - "SettingsInvalidOrNotFound,Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands.InvokeScriptAnalyzerCommand", - ErrorCategory.InvalidArgument, - settings); + string errorId; + ErrorCategory errorCategory; + switch (e) + { + case ArgumentException _: + errorId = "InvalidSettingsArgument"; + errorCategory = ErrorCategory.InvalidArgument; + break; + case InvalidDataException _: + errorId = "InvalidSettingsData"; + errorCategory = ErrorCategory.InvalidData; + break; + case InvalidOperationException _: + errorId = "InvalidPathForProvider"; // InvalidOperationException can arise from provider-specific limitations interacting with a settings path (e.g. wildcards, home, containers, etc.). + errorCategory = ErrorCategory.InvalidOperation; + break; + case InternalBufferOverflowException _: + case PathTooLongException _: + errorId = "PathOrSettingsExceededLimits"; + errorCategory = ErrorCategory.LimitsExceeded; + break; + case NotSupportedException _: + errorId = "PathOrSettingNotSupported"; + errorCategory = ErrorCategory.NotEnabled; + break; + case DirectoryNotFoundException _: + case System.IO.DriveNotFoundException _: + case System.Management.Automation.DriveNotFoundException _: + case FileNotFoundException _: + case ItemNotFoundException _: + case ProviderNotFoundException _: + errorId = "SettingsNotFound"; + errorCategory = ErrorCategory.ObjectNotFound; + break; + default: + errorId = "SettingsNotLoadable"; + errorCategory = ErrorCategory.NotSpecified; + break; + } + + var errorRecord = new ErrorRecord(e, errorId, errorCategory, this.settings); this.ThrowTerminatingError(errorRecord); } From 0472359734f75ef0c32913b257d5e4f5fdfc88e2 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Tue, 18 Jun 2019 06:44:01 -0500 Subject: [PATCH 04/75] Revert "Annotatively research current state of exception handling surrounding settings file load invocation" This reverts commit 3bfd45ba6ccf0de5b3853228d85130f18e342071. --- .../Commands/InvokeScriptAnalyzerCommand.cs | 12 ----------- Engine/Helper.cs | 6 ------ Engine/Settings.cs | 21 +------------------ 3 files changed, 1 insertion(+), 38 deletions(-) diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs index 6a12d1c13..9ead4d125 100644 --- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs +++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs @@ -269,7 +269,6 @@ protected override void BeginProcessing() Helper.Instance = new Helper( SessionState.InvokeCommand, this); - // NOTE Helper.Instance.Initialize() does *not* modify this.settings. Helper.Instance.Initialize(); var psVersionTable = this.SessionState.PSVariable.GetValue("PSVersionTable") as Hashtable; @@ -278,7 +277,6 @@ protected override void BeginProcessing() Helper.Instance.SetPSVersionTable(psVersionTable); } - // NOTE Helper.ProcessCustomRulePaths does *not* modify this.settings. string[] rulePaths = Helper.ProcessCustomRulePaths( customRulePath, this.SessionState, @@ -287,7 +285,6 @@ protected override void BeginProcessing() if (IsFileParameterSet() && Path != null) { // just used to obtain the directory to use to find settings below - // NOTE ProcessPath() does *not* modify this.settings. ProcessPath(); } @@ -296,24 +293,17 @@ protected override void BeginProcessing() var combIncludeDefaultRules = IncludeDefaultRules.IsPresent; try { - // THROW PSSASettings.Create(object, string, IOutputWriter, GetResolvedProviderPathFromPSPath) throws if ... - // NOTE Microsoft.Windows.PowerShell.ScriptAnalyzer.Settings.Create(...) types the input, gets the input (if necessary), and parses it (if any). var settingsObj = PSSASettings.Create( - // NOTHROW A null this.settings results in returning an "empty" (but not null) settingsObj without exception. - // NOTE this.settings is unmodified since start of BeginProcessing(). Thus, it is exactly the raw argument value, if any. settings, processedPaths == null || processedPaths.Count == 0 ? CurrentProviderLocation("FileSystem").ProviderPath : processedPaths[0], this, GetResolvedProviderPathFromPSPath); - // NOTE settingsObj cannot be null here since PSSASettings.Create(...) returns exactly `new Settings(settingsFound)`, which can never be null (but can throw). if (settingsObj != null) { - // NOTHROW UpdateSettings(object) can throw an ArgumentNullException, but that will never happen since settingsObj is tested for non-nullity immediately above. ScriptAnalyzer.Instance.UpdateSettings(settingsObj); // For includeDefaultRules and RecurseCustomRulePath we override the value in the settings file by // command line argument. - // NOTHROW InvokeScriptAnalyzerCommand.OverrideSwitchParam(bool, string) combRecurseCustomRulePath = OverrideSwitchParam( settingsObj.RecurseCustomRulePath, "RecurseCustomRulePath"); @@ -326,7 +316,6 @@ protected override void BeginProcessing() // simultaneously. But since, this was done before with IncludeRules, ExcludeRules and Severity, // we use the same strategy for CustomRulePath. So, we take the union of CustomRulePath provided in // the settings file and if provided on command line. - // THROW Helper.ProcessCustomRulePaths(string[], SessionState, bool) throws one of six different exceptions if a settings' custom rule path is invalid somehow (e.g. drive doesn't exit; no wildcards but item doesn't exist; provider throws a lower-level exception; etc.). See the implementation of Helper.ProcessCustomRulePaths(string[], SessionState, bool) for details. var settingsCustomRulePath = Helper.ProcessCustomRulePaths( settingsObj?.CustomRulePath?.ToArray(), this.SessionState, @@ -339,7 +328,6 @@ protected override void BeginProcessing() } catch (Exception e) { - // NOTE Any exception in resolving, getting, parsing, updating, etc. the settings herein results in an contextless WriteWarning(Strings.SettingsNotParsable), regardless of provenance. string errorId; ErrorCategory errorCategory; switch (e) diff --git a/Engine/Helper.cs b/Engine/Helper.cs index e504868d4..330547d02 100644 --- a/Engine/Helper.cs +++ b/Engine/Helper.cs @@ -1506,12 +1506,6 @@ public static string[] ProcessCustomRulePaths(string[] rulePaths, SessionState s Collection pathInfo = new Collection(); foreach (string rulePath in rulePaths) { - // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.Management.Automation.ProviderNotFoundException] if path is a provider-qualified path and the specified provider does not exist. - // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.Management.Automation.DriveNotFoundException] if path is a drive-qualified path and the specified drive does not exist. - // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.Management.Automation.ProviderInvocationException] if the provider throws an exception when its MakePath gets called. - // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.NotSupportedException] if the provider does not support multiple items. - // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.InvalidOperationException] the home location for the provider is not set and path starts with a "~". - // THROW PathIntrinsics.GetResolvedPSPathFromPSPath(rulePath) throws [System.Management.Automation.ItemNotFoundException] if path does not contain wildcard characters and could not be found. Collection pathInfosForRulePath = sessionState.Path.GetResolvedPSPathFromPSPath(rulePath); if (null != pathInfosForRulePath) { diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 74a357767..506733cc2 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -73,12 +73,10 @@ public Settings(object settings, Func presetResolver) if (File.Exists(settingsFilePath)) { filePath = settingsFilePath; - // QUESTION When does parseSettingsFile(string) throw? parseSettingsFile(settingsFilePath); } else { - // THROW ArgumentException(Strings.InvalidPath) if the resolution of the settings argument via GetResolvedProviderPathFromPSPathDelegate is non-null but not an existing file. (e.g. it may be a directory) throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, @@ -91,12 +89,10 @@ public Settings(object settings, Func presetResolver) var settingsHashtable = settings as Hashtable; if (settingsHashtable != null) { - // QUESTION When does parseSettingsHashtable(Hashtable) throw? parseSettingsHashtable(settingsHashtable); } else { - // THROW ArgumentException(Strings.SettingsInvalidType) if settings is not convertible (`as` keyword) to Hashtable. throw new ArgumentException(Strings.SettingsInvalidType); } } @@ -183,12 +179,11 @@ public static string GetSettingPresetFilePath(string settingPreset) /// An output writer. /// The GetResolvedProviderPathFromPSPath method from PSCmdlet to resolve relative path including wildcard support. /// An object of Settings type. - // THROW Settings.Create(object, string, IOutputWriter, GetResolvedProviderPathFromPSPath) throws only because of the contained invocation to the Settings constructor, which does throw. internal static Settings Create(object settingsObj, string cwd, IOutputWriter outputWriter, PathResolver.GetResolvedProviderPathFromPSPath> getResolvedProviderPathFromPSPathDelegate) { object settingsFound; - var settingsMode = FindSettingsMode(settingsObj, cwd, out settingsFound); // NOTHROW + var settingsMode = FindSettingsMode(settingsObj, cwd, out settingsFound); switch (settingsMode) { @@ -210,7 +205,6 @@ internal static Settings Create(object settingsObj, string cwd, IOutputWriter ou var userProvidedSettingsString = settingsFound.ToString(); try { - // THROW ..Single() throws [System.InvalidOperationException] if the settings path does not resolve to exactly one item. (more or less) var resolvedPath = getResolvedProviderPathFromPSPathDelegate(userProvidedSettingsString, out ProviderInfo providerInfo).Single(); settingsFound = resolvedPath; outputWriter?.WriteVerbose( @@ -219,11 +213,8 @@ internal static Settings Create(object settingsObj, string cwd, IOutputWriter ou Strings.SettingsUsingFile, resolvedPath)); } - // NOTE This catch block effectively makes *any* exception resulting from resolving the settings file path reduce to the case of no settings (by way of null settingsFound). Only a WriteVerbose(Strings.SettingsCannotFindFile) identifies what happened. catch { - // TODO Upgrade WriteVerbose(Strings.SettingsCannotFindFile) to WriteWarning(Strings.SettingsCannotFindFile). - // TODO Perform a ShouldContinue (?) confirmation check in the event that an exception occurred while resolving the settings file path. outputWriter?.WriteVerbose( String.Format( CultureInfo.CurrentCulture, @@ -247,7 +238,6 @@ internal static Settings Create(object settingsObj, string cwd, IOutputWriter ou return null; } - // THROW new Settings(object) return new Settings(settingsFound); } @@ -466,19 +456,16 @@ private void parseSettingsHashtable(Hashtable settingsHashtable) } } - // NOTE parseSettingsFile concludes by calling parseSettingsHashtable if it (parseSettingsFile) is successful. private void parseSettingsFile(string settingsFilePath) { Token[] parserTokens = null; ParseError[] parserErrors = null; - // QUESTION Can Parser.ParseFile throw? Ast profileAst = Parser.ParseFile(settingsFilePath, out parserTokens, out parserErrors); IEnumerable hashTableAsts = profileAst.FindAll(item => item is HashtableAst, false); // no hashtable, raise warning if (hashTableAsts.Count() == 0) { - // THROW parseSettingsFile throws ArgumentException(Strings.InvalidProfile) if no hashTableAst is detected in settings file. throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.InvalidProfile, settingsFilePath)); } @@ -488,18 +475,15 @@ private void parseSettingsFile(string settingsFilePath) { // ideally we should use HashtableAst.SafeGetValue() but since // it is not available on PSv3, we resort to our own narrow implementation. - // QUESTION When does GetSafeValueFromHashtableAst(hashTableAst) throw? hashtable = GetSafeValueFromHashtableAst(hashTableAst); } catch (InvalidOperationException e) { - // THROW parseSettingsFile throws ArgumentException(Strings.InvalidProfile) if GetSafeValueFromHashtableAst(hashTableAst) throws an InvalidOperationException. throw new ArgumentException(Strings.InvalidProfile, e); } if (hashtable == null) { - // THROW parseSettingsFile throws ArgumentException if GetSafeValueFromHashtableAst(hashTableAst) returns null. throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, @@ -507,7 +491,6 @@ private void parseSettingsFile(string settingsFilePath) settingsFilePath)); } - // QUESTION When does parseSettingsHashtable(Hashtable) throw? parseSettingsHashtable(hashtable); } @@ -703,7 +686,6 @@ private static bool IsBuiltinSettingPreset(object settingPreset) return false; } - // NOTHROW FindSettingsMode(object, string, out object) internal static SettingsMode FindSettingsMode(object settings, string path, out object settingsFound) { var settingsMode = SettingsMode.None; @@ -724,7 +706,6 @@ internal static SettingsMode FindSettingsMode(object settings, string path, out { // if settings are not provided explicitly, look for it in the given path // check if pssasettings.psd1 exists - // COMBAK Refactor the magic string literal "PSScriptAnalyzerSettings.psd1" to a public const field on the class. (bare minimum... although will also help open possibility for user-configurable default name) var settingsFilename = "PSScriptAnalyzerSettings.psd1"; var settingsFilePath = Path.Combine(directory, settingsFilename); settingsFound = settingsFilePath; From a012dec9bdbb135e14644fa59b7442a197d5e695 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Tue, 18 Jun 2019 12:07:21 -0500 Subject: [PATCH 05/75] Do not attempt id and category inference for the error to throw when processing the argument of `Invoke-ScriptAnalyzer -Settings` results in an exception --- .../Commands/InvokeScriptAnalyzerCommand.cs | 47 ++----------------- 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs index 9ead4d125..a818998f9 100644 --- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs +++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs @@ -328,48 +328,11 @@ protected override void BeginProcessing() } catch (Exception e) { - string errorId; - ErrorCategory errorCategory; - switch (e) - { - case ArgumentException _: - errorId = "InvalidSettingsArgument"; - errorCategory = ErrorCategory.InvalidArgument; - break; - case InvalidDataException _: - errorId = "InvalidSettingsData"; - errorCategory = ErrorCategory.InvalidData; - break; - case InvalidOperationException _: - errorId = "InvalidPathForProvider"; // InvalidOperationException can arise from provider-specific limitations interacting with a settings path (e.g. wildcards, home, containers, etc.). - errorCategory = ErrorCategory.InvalidOperation; - break; - case InternalBufferOverflowException _: - case PathTooLongException _: - errorId = "PathOrSettingsExceededLimits"; - errorCategory = ErrorCategory.LimitsExceeded; - break; - case NotSupportedException _: - errorId = "PathOrSettingNotSupported"; - errorCategory = ErrorCategory.NotEnabled; - break; - case DirectoryNotFoundException _: - case System.IO.DriveNotFoundException _: - case System.Management.Automation.DriveNotFoundException _: - case FileNotFoundException _: - case ItemNotFoundException _: - case ProviderNotFoundException _: - errorId = "SettingsNotFound"; - errorCategory = ErrorCategory.ObjectNotFound; - break; - default: - errorId = "SettingsNotLoadable"; - errorCategory = ErrorCategory.NotSpecified; - break; - } - - var errorRecord = new ErrorRecord(e, errorId, errorCategory, this.settings); - this.ThrowTerminatingError(errorRecord); + this.ThrowTerminatingError(new ErrorRecord( + e, + "SettingsNotProcessable", + ErrorCategory.NotSpecified, + this.settings)); } ScriptAnalyzer.Instance.Initialize( From 88ab1ec889f69deb0fe2e569ceaf2f0b65595327 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sat, 22 Jun 2019 19:26:17 -0500 Subject: [PATCH 06/75] Remove `using System.IO` --- Engine/Settings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 506733cc2..ebc8d7391 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; -using System.IO; using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; From 2faab31fe7bcb38e1ee223dc6b46022f268eed10 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 01:27:29 -0500 Subject: [PATCH 07/75] Revert "Remove `using System.IO`" This reverts commit 88ab1ec889f69deb0fe2e569ceaf2f0b65595327. `using System.IO` is required in Engine\Settings.cs after all. The main use is for pre-existing unqualified references to the type InvalidDataException. --- Engine/Settings.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index ebc8d7391..506733cc2 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; +using System.IO; using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; From 05f679517ad32da261ffedeb483071f67525a1c5 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:02:17 -0500 Subject: [PATCH 08/75] Remove unused local variable, 'validKeys' --- Engine/Settings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 506733cc2..4b8aac8c8 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -391,7 +391,6 @@ private Dictionary> ConvertToRuleArgumentType private void parseSettingsHashtable(Hashtable settingsHashtable) { - HashSet validKeys = new HashSet(StringComparer.OrdinalIgnoreCase); var settings = GetDictionaryFromHashtable(settingsHashtable); foreach (var settingKey in settings.Keys) { From cc3323bca5040334be429ff0bbfabf2908702431 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:14:56 -0500 Subject: [PATCH 09/75] Remove GetDictionaryFromHashtable(Hashtable) --- Engine/Settings.cs | 58 +++++++--------------------------------------- 1 file changed, 9 insertions(+), 49 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 4b8aac8c8..add303001 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -241,50 +241,6 @@ internal static Settings Create(object settingsObj, string cwd, IOutputWriter ou return new Settings(settingsFound); } - /// - /// Recursively convert hashtable to dictionary - /// - /// - /// Dictionary that maps string to object - private Dictionary GetDictionaryFromHashtable(Hashtable hashtable) - { - var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var obj in hashtable.Keys) - { - string key = obj as string; - if (key == null) - { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.KeyNotString, - key)); - } - - var valueHashtableObj = hashtable[obj]; - if (valueHashtableObj == null) - { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.WrongValueHashTable, - "", - key)); - } - - var valueHashtable = valueHashtableObj as Hashtable; - if (valueHashtable == null) - { - dictionary.Add(key, valueHashtableObj); - } - else - { - dictionary.Add(key, GetDictionaryFromHashtable(valueHashtable)); - } - } - return dictionary; - } - private bool IsStringOrStringArray(object val) { if (val is string) @@ -391,11 +347,15 @@ private Dictionary> ConvertToRuleArgumentType private void parseSettingsHashtable(Hashtable settingsHashtable) { - var settings = GetDictionaryFromHashtable(settingsHashtable); - foreach (var settingKey in settings.Keys) - { - var key = settingKey.ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 - object val = settings[key]; + // TODO Validate that no key is null. (Strings.KeyNotString) + // TODO Validate that every key is a string. (Strings.KeyNotString) + // TODO Validate that no value is null. (Strings.WrongValueHashTable) + // TODO Recurse on hashtable values. + // TODO Validate that no two keys are case-insensitive duplicates. + foreach (var settingKey in settingsHashtable.Keys) + { + var key = (settingKey as string)?.ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 + object val = settingsHashtable[key]; switch (key) { case "severity": From 70035d6c2a21c76936d8730328c9b6091f3041ac Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:28:49 -0500 Subject: [PATCH 10/75] Validate that no key is null --- Engine/Settings.cs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index add303001..72092960c 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -347,7 +347,6 @@ private Dictionary> ConvertToRuleArgumentType private void parseSettingsHashtable(Hashtable settingsHashtable) { - // TODO Validate that no key is null. (Strings.KeyNotString) // TODO Validate that every key is a string. (Strings.KeyNotString) // TODO Validate that no value is null. (Strings.WrongValueHashTable) // TODO Recurse on hashtable values. @@ -394,6 +393,7 @@ private void parseSettingsHashtable(Hashtable settingsHashtable) case "rules": try { + // TODO Validate that no key is null. (Strings.KeyNotString) ruleArguments = ConvertToRuleArgumentType(val); } catch (ArgumentException argumentException) @@ -406,11 +406,22 @@ private void parseSettingsHashtable(Hashtable settingsHashtable) break; default: - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.WrongKeyHashTable, - key)); + if ((key as string) is null) + { + throw new InvalidDataException( + string.Format( + CultureInfo.CurrentCulture, + Strings.KeyNotString, + key)); + } + else + { + throw new InvalidDataException( + string.Format( + CultureInfo.CurrentCulture, + Strings.WrongKeyHashTable, + key)); + } } } } From e3037f7897961db0be1e3870ef21011be4d9ba67 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:55:19 -0500 Subject: [PATCH 11/75] Validate that every key is a string --- Engine/Settings.cs | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 72092960c..36aec6eff 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -345,16 +345,24 @@ private Dictionary> ConvertToRuleArgumentType return ruleArgsDict; } - private void parseSettingsHashtable(Hashtable settingsHashtable) + private void parseSettingsHashtable(Hashtable settings) { - // TODO Validate that every key is a string. (Strings.KeyNotString) // TODO Validate that no value is null. (Strings.WrongValueHashTable) // TODO Recurse on hashtable values. // TODO Validate that no two keys are case-insensitive duplicates. - foreach (var settingKey in settingsHashtable.Keys) + foreach (DictionaryEntry setting in settings) { - var key = (settingKey as string)?.ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 - object val = settingsHashtable[key]; + if (!(setting.Key is string)) + { + throw new InvalidDataException( + string.Format( + CultureInfo.CurrentCulture, + Strings.KeyNotString, + setting.Key)); + } + string key = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 + + object val = setting.Value; switch (key) { case "severity": @@ -380,7 +388,7 @@ private void parseSettingsHashtable(Hashtable settingsHashtable) throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, Strings.SettingsValueTypeMustBeBool, - settingKey)); + setting.Key)); } var booleanVal = (bool)val; @@ -394,6 +402,7 @@ private void parseSettingsHashtable(Hashtable settingsHashtable) try { // TODO Validate that no key is null. (Strings.KeyNotString) + // TODO Validate that every key is a string. (Strings.KeyNotString) ruleArguments = ConvertToRuleArgumentType(val); } catch (ArgumentException argumentException) @@ -406,22 +415,11 @@ private void parseSettingsHashtable(Hashtable settingsHashtable) break; default: - if ((key as string) is null) - { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.KeyNotString, - key)); - } - else - { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.WrongKeyHashTable, - key)); - } + throw new InvalidDataException( + string.Format( + CultureInfo.CurrentCulture, + Strings.WrongKeyHashTable, + key)); } } } From b6ac61ba2b40023babc93fd2d89732d231712fca Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:55:46 -0500 Subject: [PATCH 12/75] Validate that no value is null --- Engine/Settings.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 36aec6eff..107cc276c 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -347,7 +347,6 @@ private Dictionary> ConvertToRuleArgumentType private void parseSettingsHashtable(Hashtable settings) { - // TODO Validate that no value is null. (Strings.WrongValueHashTable) // TODO Recurse on hashtable values. // TODO Validate that no two keys are case-insensitive duplicates. foreach (DictionaryEntry setting in settings) @@ -362,7 +361,17 @@ private void parseSettingsHashtable(Hashtable settings) } string key = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 + if (setting.Value is null) + { + throw new InvalidDataException( + string.Format( + CultureInfo.CurrentCulture, + Strings.WrongValueHashTable, + setting.Value, + setting.Key)); + } object val = setting.Value; + switch (key) { case "severity": @@ -403,6 +412,7 @@ private void parseSettingsHashtable(Hashtable settings) { // TODO Validate that no key is null. (Strings.KeyNotString) // TODO Validate that every key is a string. (Strings.KeyNotString) + // TODO Validate that no value is null. (Strings.WrongValueHashTable) ruleArguments = ConvertToRuleArgumentType(val); } catch (ArgumentException argumentException) From 4cf6aca7f4b9a8a240a6cb0d8d9b8411f3f316ed Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sat, 22 Jun 2019 16:16:48 -0500 Subject: [PATCH 13/75] Validate that every key is unique, ignoring case --- Engine/Settings.cs | 12 +- Engine/Strings.Designer.cs | 1209 ++++++++++++++++++------------------ Engine/Strings.resx | 3 + 3 files changed, 622 insertions(+), 602 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 107cc276c..85f98dac7 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -348,7 +348,7 @@ private Dictionary> ConvertToRuleArgumentType private void parseSettingsHashtable(Hashtable settings) { // TODO Recurse on hashtable values. - // TODO Validate that no two keys are case-insensitive duplicates. + ISet uniqueKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry setting in settings) { if (!(setting.Key is string)) @@ -361,6 +361,15 @@ private void parseSettingsHashtable(Hashtable settings) } string key = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 + if (!uniqueKeys.Add(key)) + { + throw new InvalidDataException( + string.Format( + CultureInfo.CurrentCulture, + Strings.KeyNotUniqueIgnoringCase, + key)); + } + if (setting.Value is null) { throw new InvalidDataException( @@ -413,6 +422,7 @@ private void parseSettingsHashtable(Hashtable settings) // TODO Validate that no key is null. (Strings.KeyNotString) // TODO Validate that every key is a string. (Strings.KeyNotString) // TODO Validate that no value is null. (Strings.WrongValueHashTable) + // TODO Validate that no two keys are case-insensitive duplicates. ruleArguments = ConvertToRuleArgumentType(val); } catch (ArgumentException argumentException) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index c34570bcd..fbfcabe57 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -1,7 +1,7 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// This code was generated by a New-StronglyTypedCsFileForResx function. +// To add or remove a member, edit your .ResX file then rerun Start-ResGen. // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -9,676 +9,683 @@ //------------------------------------------------------------------------------ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer { - using System; - - +using System; +using System.Reflection; + +/// +/// A strongly-typed resource class, for looking up localized strings, etc. +/// +[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] +[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] +[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + +internal class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Strings { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Strings() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Windows.PowerShell.ScriptAnalyzer.Strings", typeof(Strings).Assembly); - resourceMan = temp; - } - return resourceMan; + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Windows.PowerShell.ScriptAnalyzer.Strings", typeof(Strings).GetTypeInfo().Assembly); + resourceMan = temp; } + return resourceMan; } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; } - - /// - /// Looks up a localized string similar to Checking assembly file '{0}' .... - /// - internal static string CheckAssemblyFile { - get { - return ResourceManager.GetString("CheckAssemblyFile", resourceCulture); - } + set { + resourceCulture = value; } - - /// - /// Looks up a localized string similar to Checking module '{0}' .... - /// - internal static string CheckModuleName { - get { - return ResourceManager.GetString("CheckModuleName", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Checking assembly file '{0}' ... + /// + internal static string CheckAssemblyFile { + get { + return ResourceManager.GetString("CheckAssemblyFile", resourceCulture); } - - /// - /// Looks up a localized string similar to CommandInfo not found for function: {0}. - /// - internal static string CommandInfoNotFound { - get { - return ResourceManager.GetString("CommandInfoNotFound", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Checking module '{0}' ... + /// + internal static string CheckModuleName { + get { + return ResourceManager.GetString("CheckModuleName", resourceCulture); } - - /// - /// Looks up a localized string similar to "Argument should not be null.".. - /// - internal static string ConfigurableScriptRuleNRE { - get { - return ResourceManager.GetString("ConfigurableScriptRuleNRE", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to CommandInfo not found for function: {0} + /// + internal static string CommandInfoNotFound { + get { + return ResourceManager.GetString("CommandInfoNotFound", resourceCulture); } - - /// - /// Looks up a localized string similar to "Cannot find a ConfigurableRuleProperty attribute on property {0}".. - /// - internal static string ConfigurableScriptRulePropertyHasNotAttribute { - get { - return ResourceManager.GetString("ConfigurableScriptRulePropertyHasNotAttribute", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Writes all diagnostics to WriteObject. + /// + internal static string DefaultLoggerDescription { + get { + return ResourceManager.GetString("DefaultLoggerDescription", resourceCulture); } - - /// - /// Looks up a localized string similar to SettingsFileHasInvalidHashtable. - /// - internal static string ConfigurationFileHasInvalidHashtable { - get { - return ResourceManager.GetString("ConfigurationFileHasInvalidHashtable", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to WriteObjects + /// + internal static string DefaultLoggerName { + get { + return ResourceManager.GetString("DefaultLoggerName", resourceCulture); } - - /// - /// Looks up a localized string similar to SettingsFileHasNoHashTable. - /// - internal static string ConfigurationFileHasNoHashTable { - get { - return ResourceManager.GetString("ConfigurationFileHasNoHashTable", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Cannot find file '{0}'. + /// + internal static string FileNotFound { + get { + return ResourceManager.GetString("FileNotFound", resourceCulture); } - - /// - /// Looks up a localized string similar to SettingsFileNotFound. - /// - internal static string ConfigurationFileNotFound { - get { - return ResourceManager.GetString("ConfigurationFileNotFound", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Cannot find the path '{0}'. + /// + internal static string InvalidPath { + get { + return ResourceManager.GetString("InvalidPath", resourceCulture); } - - /// - /// Looks up a localized string similar to SettingsKeyNotAString. - /// - internal static string ConfigurationKeyNotAString { - get { - return ResourceManager.GetString("ConfigurationKeyNotAString", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to No loggers found. + /// + internal static string LoggersNotFound { + get { + return ResourceManager.GetString("LoggersNotFound", resourceCulture); } - - /// - /// Looks up a localized string similar to SettingsValueNotAString. - /// - internal static string ConfigurationValueNotAString { - get { - return ResourceManager.GetString("ConfigurationValueNotAString", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Cannot find rule extension '{0}'. + /// + internal static string MissingRuleExtension { + get { + return ResourceManager.GetString("MissingRuleExtension", resourceCulture); } - - /// - /// Looks up a localized string similar to SettingsValueWrongFormat. - /// - internal static string ConfigurationValueWrongFormat { - get { - return ResourceManager.GetString("ConfigurationValueWrongFormat", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to {0} cannot be set by both positional and named arguments. + /// + internal static string NamedAndPositionalArgumentsConflictError { + get { + return ResourceManager.GetString("NamedAndPositionalArgumentsConflictError", resourceCulture); } - - /// - /// Looks up a localized string similar to Writes all diagnostics to WriteObject.. - /// - internal static string DefaultLoggerDescription { - get { - return ResourceManager.GetString("DefaultLoggerDescription", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Named arguments must always come after positional arguments. + /// + internal static string NamedArgumentsBeforePositionalError { + get { + return ResourceManager.GetString("NamedArgumentsBeforePositionalError", resourceCulture); } - - /// - /// Looks up a localized string similar to WriteObjects. - /// - internal static string DefaultLoggerName { - get { - return ResourceManager.GetString("DefaultLoggerName", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Parse error in file {0}: {1} at line {2} column {3}. + /// + internal static string ParserErrorFormat { + get { + return ResourceManager.GetString("ParserErrorFormat", resourceCulture); } - - /// - /// Looks up a localized string similar to Edge from {0} to {1} already exists.. - /// - internal static string DigraphEdgeAlreadyExists { - get { - return ResourceManager.GetString("DigraphEdgeAlreadyExists", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to There are too many parser errors in {0}. Please correct them before running ScriptAnalyzer. + /// + internal static string ParserErrorMessage { + get { + return ResourceManager.GetString("ParserErrorMessage", resourceCulture); } - - /// - /// Looks up a localized string similar to Vertex {0} already exists! Cannot add it to the digraph.. - /// - internal static string DigraphVertexAlreadyExists { - get { - return ResourceManager.GetString("DigraphVertexAlreadyExists", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to RULE_ERROR + /// + internal static string RuleErrorMessage { + get { + return ResourceManager.GetString("RuleErrorMessage", resourceCulture); } - - /// - /// Looks up a localized string similar to Vertex {0} does not exist in the digraph.. - /// - internal static string DigraphVertexDoesNotExists { - get { - return ResourceManager.GetString("DigraphVertexDoesNotExists", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Cannot find ScriptAnalyzer rules in the specified path + /// + internal static string RulesNotFound { + get { + return ResourceManager.GetString("RulesNotFound", resourceCulture); } - - /// - /// Looks up a localized string similar to Cannot determine line endings as the text probably contain mixed line endings.. - /// - internal static string EditableTextInvalidLineEnding { - get { - return ResourceManager.GetString("EditableTextInvalidLineEnding", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Suppression Message Attribute error at line {0} in {1} : {2} + /// + internal static string RuleSuppressionErrorFormat { + get { + return ResourceManager.GetString("RuleSuppressionErrorFormat", resourceCulture); } - - /// - /// Looks up a localized string similar to TextEdit extent not completely contained in EditableText.. - /// - internal static string EditableTextRangeIsNotContained { - get { - return ResourceManager.GetString("EditableTextRangeIsNotContained", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to All the arguments of the Suppress Message Attribute should be string constants. + /// + internal static string StringConstantArgumentsSuppressionAttributeError { + get { + return ResourceManager.GetString("StringConstantArgumentsSuppressionAttributeError", resourceCulture); } - - /// - /// Looks up a localized string similar to Cannot find file '{0}'.. - /// - internal static string FileNotFound { - get { - return ResourceManager.GetString("FileNotFound", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to If Target is specified, Scope must be specified. + /// + internal static string TargetWithoutScopeSuppressionAttributeError { + get { + return ResourceManager.GetString("TargetWithoutScopeSuppressionAttributeError", resourceCulture); } - - /// - /// Looks up a localized string similar to Cannot find the path '{0}'.. - /// - internal static string InvalidPath { - get { - return ResourceManager.GetString("InvalidPath", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Analyzing file: {0} + /// + internal static string VerboseFileMessage { + get { + return ResourceManager.GetString("VerboseFileMessage", resourceCulture); } - - /// - /// Looks up a localized string similar to Settings file '{0}' is invalid because it does not contain a hashtable.. - /// - internal static string InvalidProfile { - get { - return ResourceManager.GetString("InvalidProfile", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Running {0} rule. + /// + internal static string VerboseRunningMessage { + get { + return ResourceManager.GetString("VerboseRunningMessage", resourceCulture); } - - /// - /// Looks up a localized string similar to Key {0} in the settings is not a string.. - /// - internal static string KeyNotString { - get { - return ResourceManager.GetString("KeyNotString", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Scope can only be either function or class. + /// + internal static string WrongScopeArgumentSuppressionAttributeError { + get { + return ResourceManager.GetString("WrongScopeArgumentSuppressionAttributeError", resourceCulture); } - - /// - /// Looks up a localized string similar to No loggers found.. - /// - internal static string LoggersNotFound { - get { - return ResourceManager.GetString("LoggersNotFound", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to RuleName must not be null. + /// + internal static string NullRuleNameError { + get { + return ResourceManager.GetString("NullRuleNameError", resourceCulture); } - - /// - /// Looks up a localized string similar to Cannot find rule extension '{0}'.. - /// - internal static string MissingRuleExtension { - get { - return ResourceManager.GetString("MissingRuleExtension", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Cannot find any Targets {0} that match the Scope {1} to apply the SuppressMessageAttribute. + /// + internal static string TargetCannotBeFoundError { + get { + return ResourceManager.GetString("TargetCannotBeFoundError", resourceCulture); } - - /// - /// Looks up a localized string similar to Temporary module location: {0}.. - /// - internal static string ModuleDepHandlerTempLocation { - get { - return ResourceManager.GetString("ModuleDepHandlerTempLocation", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Cannot find any DiagnosticRecord with the Rule Suppression ID {0}. + /// + internal static string RuleSuppressionIDError { + get { + return ResourceManager.GetString("RuleSuppressionIDError", resourceCulture); } - - /// - /// Looks up a localized string similar to {0} cannot be set by both positional and named arguments.. - /// - internal static string NamedAndPositionalArgumentsConflictError { - get { - return ResourceManager.GetString("NamedAndPositionalArgumentsConflictError", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to {0} is not a valid key in the settings hashtable: file {3}. Valid keys are ExcludeRules, IncludeRules and Severity. + /// + internal static string WrongKey { + get { + return ResourceManager.GetString("WrongKey", resourceCulture); } - - /// - /// Looks up a localized string similar to Named arguments must always come after positional arguments.. - /// - internal static string NamedArgumentsBeforePositionalError { - get { - return ResourceManager.GetString("NamedArgumentsBeforePositionalError", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Key in the settings hashtable should be a string: line {0} column {1} in file {2} + /// + internal static string WrongKeyFormat { + get { + return ResourceManager.GetString("WrongKeyFormat", resourceCulture); } - - /// - /// Looks up a localized string similar to RuleName must not be null.. - /// - internal static string NullRuleNameError { - get { - return ResourceManager.GetString("NullRuleNameError", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Value in the settings hashtable should be a string or an array of strings: line {0} column {1} in file {2} + /// + internal static string WrongValueFormat { + get { + return ResourceManager.GetString("WrongValueFormat", resourceCulture); } - - /// - /// Looks up a localized string similar to Parse error in script definition: {0} at line {1} column {2}.. - /// - internal static string ParseErrorFormatForScriptDefinition { - get { - return ResourceManager.GetString("ParseErrorFormatForScriptDefinition", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Settings file '{0}' is invalid because it does not contain a hashtable. + /// + internal static string InvalidProfile { + get { + return ResourceManager.GetString("InvalidProfile", resourceCulture); } - - /// - /// Looks up a localized string similar to Parse error in file {0}: {1} at line {2} column {3}.. - /// - internal static string ParserErrorFormat { - get { - return ResourceManager.GetString("ParserErrorFormat", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Parse error in script definition: {0} at line {1} column {2}. + /// + internal static string ParseErrorFormatForScriptDefinition { + get { + return ResourceManager.GetString("ParseErrorFormatForScriptDefinition", resourceCulture); } - - /// - /// Looks up a localized string similar to There are too many parser errors in {0}. Please correct them before running ScriptAnalyzer.. - /// - internal static string ParserErrorMessage { - get { - return ResourceManager.GetString("ParserErrorMessage", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to There are too many parser errors in the script definition. Please correct them before running ScriptAnalyzer. + /// + internal static string ParserErrorMessageForScriptDefinition { + get { + return ResourceManager.GetString("ParserErrorMessageForScriptDefinition", resourceCulture); } - - /// - /// Looks up a localized string similar to There are too many parser errors in the script definition. Please correct them before running ScriptAnalyzer.. - /// - internal static string ParserErrorMessageForScriptDefinition { - get { - return ResourceManager.GetString("ParserErrorMessageForScriptDefinition", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Suppression Message Attribute error at line {0} in script definition : {1} + /// + internal static string RuleSuppressionErrorFormatScriptDefinition { + get { + return ResourceManager.GetString("RuleSuppressionErrorFormatScriptDefinition", resourceCulture); } - - /// - /// Looks up a localized string similar to Column number cannot be less than 1.. - /// - internal static string PositionColumnLessThanOne { - get { - return ResourceManager.GetString("PositionColumnLessThanOne", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Analyzing Script Definition. + /// + internal static string VerboseScriptDefinitionMessage { + get { + return ResourceManager.GetString("VerboseScriptDefinitionMessage", resourceCulture); } - - /// - /// Looks up a localized string similar to Line number cannot be less than 1.. - /// - internal static string PositionLineLessThanOne { - get { - return ResourceManager.GetString("PositionLineLessThanOne", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to SettingsFileHasInvalidHashtable + /// + internal static string ConfigurationFileHasInvalidHashtable { + get { + return ResourceManager.GetString("ConfigurationFileHasInvalidHashtable", resourceCulture); } - - /// - /// Looks up a localized string similar to Input position should be less than that of the invoking object.. - /// - internal static string PositionRefPosLessThanInputPos { - get { - return ResourceManager.GetString("PositionRefPosLessThanInputPos", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to SettingsFileHasNoHashTable + /// + internal static string ConfigurationFileHasNoHashTable { + get { + return ResourceManager.GetString("ConfigurationFileHasNoHashTable", resourceCulture); } - - /// - /// Looks up a localized string similar to Reference Position should begin before start Position of Range.. - /// - internal static string RangeRefPosShouldStartBeforeRangeStartPos { - get { - return ResourceManager.GetString("RangeRefPosShouldStartBeforeRangeStartPos", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to SettingsFileNotFound + /// + internal static string ConfigurationFileNotFound { + get { + return ResourceManager.GetString("ConfigurationFileNotFound", resourceCulture); } - - /// - /// Looks up a localized string similar to Start position cannot be before End position.. - /// - internal static string RangeStartPosGreaterThanEndPos { - get { - return ResourceManager.GetString("RangeStartPosGreaterThanEndPos", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to SettingsKeyNotAString + /// + internal static string ConfigurationKeyNotAString { + get { + return ResourceManager.GetString("ConfigurationKeyNotAString", resourceCulture); } - - /// - /// Looks up a localized string similar to RULE_ERROR. - /// - internal static string RuleErrorMessage { - get { - return ResourceManager.GetString("RuleErrorMessage", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to SettingsValueNotAString + /// + internal static string ConfigurationValueNotAString { + get { + return ResourceManager.GetString("ConfigurationValueNotAString", resourceCulture); } - - /// - /// Looks up a localized string similar to Cannot find ScriptAnalyzer rules in the specified path. - /// - internal static string RulesNotFound { - get { - return ResourceManager.GetString("RulesNotFound", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to SettingsValueWrongFormat + /// + internal static string ConfigurationValueWrongFormat { + get { + return ResourceManager.GetString("ConfigurationValueWrongFormat", resourceCulture); } - - /// - /// Looks up a localized string similar to Suppression Message Attribute error at line {0} in {1} : {2}. - /// - internal static string RuleSuppressionErrorFormat { - get { - return ResourceManager.GetString("RuleSuppressionErrorFormat", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to WrongSettingsKey + /// + internal static string WrongConfigurationKey { + get { + return ResourceManager.GetString("WrongConfigurationKey", resourceCulture); } - - /// - /// Looks up a localized string similar to Suppression Message Attribute error at line {0} in script definition : {1}. - /// - internal static string RuleSuppressionErrorFormatScriptDefinition { - get { - return ResourceManager.GetString("RuleSuppressionErrorFormatScriptDefinition", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Key {0} in the settings is not a string. + /// + internal static string KeyNotString { + get { + return ResourceManager.GetString("KeyNotString", resourceCulture); } - - /// - /// Looks up a localized string similar to Cannot find any DiagnosticRecord with the Rule Suppression ID {0}.. - /// - internal static string RuleSuppressionIDError { - get { - return ResourceManager.GetString("RuleSuppressionIDError", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Key {0} in the settings is not unique, ignore casing. + /// + internal static string KeyNotUniqueIgnoringCase { + get { + return ResourceManager.GetString("KeyNotUniqueIgnoringCase", resourceCulture); } - - /// - /// Looks up a localized string similar to Found {0}. Will use it to provide settings for this invocation.. - /// - internal static string SettingsAutoDiscovered { - get { - return ResourceManager.GetString("SettingsAutoDiscovered", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to {0} is not a valid key in the settings hashtable. Valid keys are ExcludeRules, IncludeRules and Severity. + /// + internal static string WrongKeyHashTable { + get { + return ResourceManager.GetString("WrongKeyHashTable", resourceCulture); } - - /// - /// Looks up a localized string similar to Cannot resolve settings file path '{0}'.. - /// - internal static string SettingsCannotFindFile { - get { - return ResourceManager.GetString("SettingsCannotFindFile", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Value {0} for key {1} has the wrong data type. + /// + internal static string WrongValueHashTable { + get { + return ResourceManager.GetString("WrongValueHashTable", resourceCulture); } - - /// - /// Looks up a localized string similar to Dictionary should be indexable in a case-insensitive manner.. - /// - internal static string SettingsDictionaryShouldBeCaseInsesitive { - get { - return ResourceManager.GetString("SettingsDictionaryShouldBeCaseInsesitive", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Input should be a dictionary type. + /// + internal static string SettingsInputShouldBeDictionary { + get { + return ResourceManager.GetString("SettingsInputShouldBeDictionary", resourceCulture); } - - /// - /// Looks up a localized string similar to Input should be a dictionary type.. - /// - internal static string SettingsInputShouldBeDictionary { - get { - return ResourceManager.GetString("SettingsInputShouldBeDictionary", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Dictionary should be indexable in a case-insensitive manner. + /// + internal static string SettingsDictionaryShouldBeCaseInsesitive { + get { + return ResourceManager.GetString("SettingsDictionaryShouldBeCaseInsesitive", resourceCulture); } - - /// - /// Looks up a localized string similar to Settings should be either a file path, built-in preset or a hashtable.. - /// - internal static string SettingsInvalidType { - get { - return ResourceManager.GetString("SettingsInvalidType", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Settings should be either a file path, built-in preset or a hashtable. + /// + internal static string SettingsInvalidType { + get { + return ResourceManager.GetString("SettingsInvalidType", resourceCulture); } - - /// - /// Looks up a localized string similar to Cannot parse settings. Will abort the invocation.. - /// - internal static string SettingsNotParsable { - get { - return ResourceManager.GetString("SettingsNotParsable", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Settings not provided. Will look for settings file in the given path {0}. + /// + internal static string SettingsNotProvided { + get { + return ResourceManager.GetString("SettingsNotProvided", resourceCulture); } - - /// - /// Looks up a localized string similar to Settings not provided. Will look for settings file in the given path {0}.. - /// - internal static string SettingsNotProvided { - get { - return ResourceManager.GetString("SettingsNotProvided", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Found {0}. Will use it to provide settings for this invocation. + /// + internal static string SettingsAutoDiscovered { + get { + return ResourceManager.GetString("SettingsAutoDiscovered", resourceCulture); } - - /// - /// Looks up a localized string similar to Settings object could not be resolved.. - /// - internal static string SettingsObjectCouldNotBResolved { - get { - return ResourceManager.GetString("SettingsObjectCouldNotBResolved", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Using settings file at {0}. + /// + internal static string SettingsUsingFile { + get { + return ResourceManager.GetString("SettingsUsingFile", resourceCulture); } - - /// - /// Looks up a localized string similar to Using settings file at {0}.. - /// - internal static string SettingsUsingFile { - get { - return ResourceManager.GetString("SettingsUsingFile", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Using settings hashtable. + /// + internal static string SettingsUsingHashtable { + get { + return ResourceManager.GetString("SettingsUsingHashtable", resourceCulture); } - - /// - /// Looks up a localized string similar to Using settings hashtable.. - /// - internal static string SettingsUsingHashtable { - get { - return ResourceManager.GetString("SettingsUsingHashtable", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Cannot resolve settings file path '{0}'. + /// + internal static string SettingsCannotFindFile { + get { + return ResourceManager.GetString("SettingsCannotFindFile", resourceCulture); } - - /// - /// Looks up a localized string similar to {0} property must be of type bool.. - /// - internal static string SettingsValueTypeMustBeBool { - get { - return ResourceManager.GetString("SettingsValueTypeMustBeBool", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Cannot parse settings. Will abort the invocation. + /// + internal static string SettingsNotParsable { + get { + return ResourceManager.GetString("SettingsNotParsable", resourceCulture); } - - /// - /// Looks up a localized string similar to All the arguments of the Suppress Message Attribute should be string constants.. - /// - internal static string StringConstantArgumentsSuppressionAttributeError { - get { - return ResourceManager.GetString("StringConstantArgumentsSuppressionAttributeError", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to {0} property must be of type bool. + /// + internal static string SettingsValueTypeMustBeBool { + get { + return ResourceManager.GetString("SettingsValueTypeMustBeBool", resourceCulture); } - - /// - /// Looks up a localized string similar to Cannot find any Targets {0} that match the Scope {1} to apply the SuppressMessageAttribute.. - /// - internal static string TargetCannotBeFoundError { - get { - return ResourceManager.GetString("TargetCannotBeFoundError", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Temporary module location: {0}. + /// + internal static string ModuleDepHandlerTempLocation { + get { + return ResourceManager.GetString("ModuleDepHandlerTempLocation", resourceCulture); } - - /// - /// Looks up a localized string similar to If Target is specified, Scope must be specified.. - /// - internal static string TargetWithoutScopeSuppressionAttributeError { - get { - return ResourceManager.GetString("TargetWithoutScopeSuppressionAttributeError", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Vertex {0} already exists! Cannot add it to the digraph. + /// + internal static string DigraphVertexAlreadyExists { + get { + return ResourceManager.GetString("DigraphVertexAlreadyExists", resourceCulture); } - - /// - /// Looks up a localized string similar to Line element cannot be null.. - /// - internal static string TextEditNoNullItem { - get { - return ResourceManager.GetString("TextEditNoNullItem", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Edge from {0} to {1} already exists. + /// + internal static string DigraphEdgeAlreadyExists { + get { + return ResourceManager.GetString("DigraphEdgeAlreadyExists", resourceCulture); } - - /// - /// Looks up a localized string similar to Line element cannot be null.. - /// - internal static string TextLinesNoNullItem { - get { - return ResourceManager.GetString("TextLinesNoNullItem", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Vertex {0} does not exist in the digraph. + /// + internal static string DigraphVertexDoesNotExists { + get { + return ResourceManager.GetString("DigraphVertexDoesNotExists", resourceCulture); } - - /// - /// Looks up a localized string similar to Ignoring 'TypeNotFound' parse error on type '{0}'. Check if the specified type is correct. This can also be due the type not being known at parse time due to types imported by 'using' statements.. - /// - internal static string TypeNotFoundParseErrorFound { - get { - return ResourceManager.GetString("TypeNotFoundParseErrorFound", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to "Cannot find a ConfigurableRuleProperty attribute on property {0}". + /// + internal static string ConfigurableScriptRulePropertyHasNotAttribute { + get { + return ResourceManager.GetString("ConfigurableScriptRulePropertyHasNotAttribute", resourceCulture); } - - /// - /// Looks up a localized string similar to Analyzing file: {0}. - /// - internal static string VerboseFileMessage { - get { - return ResourceManager.GetString("VerboseFileMessage", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to "Argument should not be null.". + /// + internal static string ConfigurableScriptRuleNRE { + get { + return ResourceManager.GetString("ConfigurableScriptRuleNRE", resourceCulture); } - - /// - /// Looks up a localized string similar to Running {0} rule.. - /// - internal static string VerboseRunningMessage { - get { - return ResourceManager.GetString("VerboseRunningMessage", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Line element cannot be null. + /// + internal static string TextLinesNoNullItem { + get { + return ResourceManager.GetString("TextLinesNoNullItem", resourceCulture); } - - /// - /// Looks up a localized string similar to Analyzing Script Definition.. - /// - internal static string VerboseScriptDefinitionMessage { - get { - return ResourceManager.GetString("VerboseScriptDefinitionMessage", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Line element cannot be null. + /// + internal static string TextEditNoNullItem { + get { + return ResourceManager.GetString("TextEditNoNullItem", resourceCulture); } - - /// - /// Looks up a localized string similar to WrongSettingsKey. - /// - internal static string WrongConfigurationKey { - get { - return ResourceManager.GetString("WrongConfigurationKey", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to TextEdit extent not completely contained in EditableText. + /// + internal static string EditableTextRangeIsNotContained { + get { + return ResourceManager.GetString("EditableTextRangeIsNotContained", resourceCulture); } - - /// - /// Looks up a localized string similar to {0} is not a valid key in the settings hashtable: file {3}. Valid keys are ExcludeRules, IncludeRules and Severity.. - /// - internal static string WrongKey { - get { - return ResourceManager.GetString("WrongKey", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Cannot determine line endings as the text probably contain mixed line endings. + /// + internal static string EditableTextInvalidLineEnding { + get { + return ResourceManager.GetString("EditableTextInvalidLineEnding", resourceCulture); } - - /// - /// Looks up a localized string similar to Key in the settings hashtable should be a string: line {0} column {1} in file {2}. - /// - internal static string WrongKeyFormat { - get { - return ResourceManager.GetString("WrongKeyFormat", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Start position cannot be before End position. + /// + internal static string RangeStartPosGreaterThanEndPos { + get { + return ResourceManager.GetString("RangeStartPosGreaterThanEndPos", resourceCulture); } - - /// - /// Looks up a localized string similar to {0} is not a valid key in the settings hashtable. Valid keys are ExcludeRules, IncludeRules and Severity.. - /// - internal static string WrongKeyHashTable { - get { - return ResourceManager.GetString("WrongKeyHashTable", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Reference Position should begin before start Position of Range. + /// + internal static string RangeRefPosShouldStartBeforeRangeStartPos { + get { + return ResourceManager.GetString("RangeRefPosShouldStartBeforeRangeStartPos", resourceCulture); } - - /// - /// Looks up a localized string similar to Scope can only be either function or class.. - /// - internal static string WrongScopeArgumentSuppressionAttributeError { - get { - return ResourceManager.GetString("WrongScopeArgumentSuppressionAttributeError", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Line number cannot be less than 1. + /// + internal static string PositionLineLessThanOne { + get { + return ResourceManager.GetString("PositionLineLessThanOne", resourceCulture); } - - /// - /// Looks up a localized string similar to Value in the settings hashtable should be a string or an array of strings: line {0} column {1} in file {2}. - /// - internal static string WrongValueFormat { - get { - return ResourceManager.GetString("WrongValueFormat", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Column number cannot be less than 1. + /// + internal static string PositionColumnLessThanOne { + get { + return ResourceManager.GetString("PositionColumnLessThanOne", resourceCulture); } - - /// - /// Looks up a localized string similar to Value {0} for key {1} has the wrong data type.. - /// - internal static string WrongValueHashTable { - get { - return ResourceManager.GetString("WrongValueHashTable", resourceCulture); - } + } + + /// + /// Looks up a localized string similar to Input position should be less than that of the invoking object. + /// + internal static string PositionRefPosLessThanInputPos { + get { + return ResourceManager.GetString("PositionRefPosLessThanInputPos", resourceCulture); } } + + /// + /// Looks up a localized string similar to Settings object could not be resolved. + /// + internal static string SettingsObjectCouldNotBResolved { + get { + return ResourceManager.GetString("SettingsObjectCouldNotBResolved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ignoring 'TypeNotFound' parse error on type '{0}'. Check if the specified type is correct. This can also be due the type not being known at parse time due to types imported by 'using' statements. + /// + internal static string TypeNotFoundParseErrorFound { + get { + return ResourceManager.GetString("TypeNotFoundParseErrorFound", resourceCulture); + } + } + +} } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 553443839..86f106308 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -237,6 +237,9 @@ Key {0} in the settings is not a string. + + Key {0} in the settings is not unique, ignore casing. + {0} is not a valid key in the settings hashtable. Valid keys are ExcludeRules, IncludeRules and Severity. From 3a5380b92ed322d37ba160296f82daf52ab808c2 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 02:47:22 -0500 Subject: [PATCH 14/75] Extract validation of "Rules" setting dictionary type from method Strings.ConvertToRuleArgumentType(Dictionary) --- Engine/Settings.cs | 20 +++++++++++--------- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 85f98dac7..c84a7b6c5 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -318,17 +318,11 @@ private List GetData(object val, string key) /// Sets the arguments for consumption by rules /// /// A hashtable with rule names as keys - private Dictionary> ConvertToRuleArgumentType(object ruleArguments) + private Dictionary> ConvertToRuleArgumentType(Dictionary ruleArgs) { - var ruleArgs = ruleArguments as Dictionary; - if (ruleArgs == null) - { - throw new ArgumentException(Strings.SettingsInputShouldBeDictionary, nameof(ruleArguments)); - } - if (ruleArgs.Comparer != StringComparer.OrdinalIgnoreCase) { - throw new ArgumentException(Strings.SettingsDictionaryShouldBeCaseInsesitive, nameof(ruleArguments)); + throw new ArgumentException(Strings.SettingsDictionaryShouldBeCaseInsesitive, nameof(ruleArgs)); } var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); @@ -417,13 +411,21 @@ private void parseSettingsHashtable(Hashtable settings) break; case "rules": + var ruleArgs = val as Dictionary; + if (ruleArgs == null) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.RulesSettingShouldBeDictionary)); + } + try { // TODO Validate that no key is null. (Strings.KeyNotString) // TODO Validate that every key is a string. (Strings.KeyNotString) // TODO Validate that no value is null. (Strings.WrongValueHashTable) // TODO Validate that no two keys are case-insensitive duplicates. - ruleArguments = ConvertToRuleArgumentType(val); + this.ruleArguments = ConvertToRuleArgumentType(ruleArgs); } catch (ArgumentException argumentException) { diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index fbfcabe57..c0ab554d3 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -453,6 +453,15 @@ internal static string SettingsInputShouldBeDictionary { } } + /// + /// Looks up a localized string similar to Value for key Rules should be a dictionary type. + /// + internal static string RulesSettingShouldBeDictionary { + get { + return ResourceManager.GetString("RulesSettingShouldBeDictionary", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 86f106308..de5f02a0d 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -249,6 +249,9 @@ Input should be a dictionary type. + + Value for key Rules should be a dictionary type. + Dictionary should be indexable in a case-insensitive manner. From 4ace3b94db4d5e0ffca53e3a5f909862de052948 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 02:52:25 -0500 Subject: [PATCH 15/75] Extract validation of dictionary key case-insensitivity from method Settings.ConvertToRuleArgumentType(Dictionary) --- Engine/Settings.cs | 12 +++++++----- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index c84a7b6c5..de99a22ea 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -320,11 +320,6 @@ private List GetData(object val, string key) /// A hashtable with rule names as keys private Dictionary> ConvertToRuleArgumentType(Dictionary ruleArgs) { - if (ruleArgs.Comparer != StringComparer.OrdinalIgnoreCase) - { - throw new ArgumentException(Strings.SettingsDictionaryShouldBeCaseInsesitive, nameof(ruleArgs)); - } - var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var rule in ruleArgs.Keys) { @@ -418,6 +413,13 @@ private void parseSettingsHashtable(Hashtable settings) CultureInfo.CurrentCulture, Strings.RulesSettingShouldBeDictionary)); } + + if (ruleArgs.Comparer != StringComparer.OrdinalIgnoreCase) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.RulesSettingDictionaryShouldBeCaseInsensitive)); + } try { diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index c0ab554d3..fa8ea3695 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -462,6 +462,15 @@ internal static string RulesSettingShouldBeDictionary { } } + /// + /// Looks up a localized string similar to Dictionary value for key Rules should be indexable in a case-insensitive manner. + /// + internal static string RulesSettingDictionaryShouldBeCaseInsensitive { + get { + return ResourceManager.GetString("RulesSettingDictionaryShouldBeCaseInsensitive", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index de5f02a0d..c76474af5 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -252,6 +252,9 @@ Value for key Rules should be a dictionary type. + + Dictionary value for key Rules should be indexable in a case-insensitive manner. + Dictionary should be indexable in a case-insensitive manner. From cfd7037c78db5a5cdfac80b7d492994794d76711 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 03:03:18 -0500 Subject: [PATCH 16/75] Extract validation of "Rules" setting values dictionary types from method Strings.ConvertToRuleArgumentType(Dictionary) --- Engine/Settings.cs | 43 ++++++++++++++++---------------------- Engine/Strings.Designer.cs | 9 ++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index de99a22ea..a9ad518e3 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -320,18 +320,7 @@ private List GetData(object val, string key) /// A hashtable with rule names as keys private Dictionary> ConvertToRuleArgumentType(Dictionary ruleArgs) { - var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); - foreach (var rule in ruleArgs.Keys) - { - var argsDict = ruleArgs[rule] as Dictionary; - if (argsDict == null) - { - throw new InvalidDataException(Strings.SettingsInputShouldBeDictionary); - } - ruleArgsDict[rule] = argsDict; - } - - return ruleArgsDict; + return null; } private void parseSettingsHashtable(Hashtable settings) @@ -406,6 +395,11 @@ private void parseSettingsHashtable(Hashtable settings) break; case "rules": + // TODO Validate that no key is null. (Strings.KeyNotString) + // TODO Validate that every key is a string. (Strings.KeyNotString) + // TODO Validate that no value is null. (Strings.WrongValueHashTable) + // TODO Validate that no two keys are case-insensitive duplicates. + var ruleArgs = val as Dictionary; if (ruleArgs == null) { @@ -420,22 +414,21 @@ private void parseSettingsHashtable(Hashtable settings) CultureInfo.CurrentCulture, Strings.RulesSettingDictionaryShouldBeCaseInsensitive)); } - - try - { - // TODO Validate that no key is null. (Strings.KeyNotString) - // TODO Validate that every key is a string. (Strings.KeyNotString) - // TODO Validate that no value is null. (Strings.WrongValueHashTable) - // TODO Validate that no two keys are case-insensitive duplicates. - this.ruleArguments = ConvertToRuleArgumentType(ruleArgs); - } - catch (ArgumentException argumentException) + + var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (var rule in ruleArgs.Keys) { - throw new InvalidDataException( - string.Format(CultureInfo.CurrentCulture, Strings.WrongValueHashTable, "", key), - argumentException); + var argsDict = ruleArgs[rule] as Dictionary; + if (argsDict == null) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.RulesSettingValuesShouldBeDictionaries)); + } + ruleArgsDict[rule] = argsDict; } + this.ruleArguments = ruleArgsDict; break; default: diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index fa8ea3695..3b71995c2 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -471,6 +471,15 @@ internal static string RulesSettingDictionaryShouldBeCaseInsensitive { } } + /// + /// Looks up a localized string similar to Dictionary values for key Rules should be dictionary types. + /// + internal static string RulesSettingValuesShouldBeDictionaries { + get { + return ResourceManager.GetString("RulesSettingValuesShouldBeDictionaries", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index c76474af5..61a66c5b4 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -255,6 +255,9 @@ Dictionary value for key Rules should be indexable in a case-insensitive manner. + + Dictionary values for key Rules should be dictionary types. + Dictionary should be indexable in a case-insensitive manner. From 07362422dd173b33abc01d116702c8a13d448172 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 03:03:55 -0500 Subject: [PATCH 17/75] Remove method Settings.ConvertToRuleArgumentType(Dictionary) --- Engine/Settings.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index a9ad518e3..224a48db9 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -314,15 +314,6 @@ private List GetData(object val, string key) return values; } - /// - /// Sets the arguments for consumption by rules - /// - /// A hashtable with rule names as keys - private Dictionary> ConvertToRuleArgumentType(Dictionary ruleArgs) - { - return null; - } - private void parseSettingsHashtable(Hashtable settings) { // TODO Recurse on hashtable values. From ef510c9846f08c1a66d1a381b5110f794690d45f Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 03:16:45 -0500 Subject: [PATCH 18/75] Refactor validation of setting "Rules" type to accommodate finer-fidelity validation --- Engine/Settings.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 224a48db9..8edbacd3a 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -391,14 +391,15 @@ private void parseSettingsHashtable(Hashtable settings) // TODO Validate that no value is null. (Strings.WrongValueHashTable) // TODO Validate that no two keys are case-insensitive duplicates. - var ruleArgs = val as Dictionary; - if (ruleArgs == null) + var rules = val as Hashtable; + if (rules == null) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, Strings.RulesSettingShouldBeDictionary)); } - + + var ruleArgs = rules as Dictionary; if (ruleArgs.Comparer != StringComparer.OrdinalIgnoreCase) { throw new InvalidDataException(string.Format( From cd031f8f14088c10bfb844dc50752db96fd52633 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 03:22:03 -0500 Subject: [PATCH 19/75] Validate that each key of the "Rules" setting is nonnull --- Engine/Settings.cs | 41 ++++++++++++++++++++++++-------------- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 8edbacd3a..8017e7ccc 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -328,15 +328,15 @@ private void parseSettingsHashtable(Hashtable settings) Strings.KeyNotString, setting.Key)); } - string key = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 + string settingKey = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 - if (!uniqueKeys.Add(key)) + if (!uniqueKeys.Add(settingKey)) { throw new InvalidDataException( string.Format( CultureInfo.CurrentCulture, Strings.KeyNotUniqueIgnoringCase, - key)); + settingKey)); } if (setting.Value is null) @@ -348,29 +348,29 @@ private void parseSettingsHashtable(Hashtable settings) setting.Value, setting.Key)); } - object val = setting.Value; + object settingValue = setting.Value; - switch (key) + switch (settingKey) { case "severity": - severities = GetData(val, key); + severities = GetData(settingValue, settingKey); break; case "includerules": - includeRules = GetData(val, key); + includeRules = GetData(settingValue, settingKey); break; case "excluderules": - excludeRules = GetData(val, key); + excludeRules = GetData(settingValue, settingKey); break; case "customrulepath": - customRulePath = GetData(val, key); + customRulePath = GetData(settingValue, settingKey); break; case "includedefaultrules": case "recursecustomrulepath": - if (!(val is bool)) + if (!(settingValue is bool)) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, @@ -378,26 +378,35 @@ private void parseSettingsHashtable(Hashtable settings) setting.Key)); } - var booleanVal = (bool)val; + var booleanVal = (bool)settingValue; var field = this.GetType().GetField( - key, + settingKey, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.NonPublic); field.SetValue(this, booleanVal); break; case "rules": - // TODO Validate that no key is null. (Strings.KeyNotString) // TODO Validate that every key is a string. (Strings.KeyNotString) // TODO Validate that no value is null. (Strings.WrongValueHashTable) // TODO Validate that no two keys are case-insensitive duplicates. - var rules = val as Hashtable; + var rules = settingValue as Hashtable; if (rules == null) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, Strings.RulesSettingShouldBeDictionary)); } + + foreach (var ruleKey in rules.Keys) + { + if (ruleKey is null) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.RulesSettingKeysShouldBeNonNull)); + } + } var ruleArgs = rules as Dictionary; if (ruleArgs.Comparer != StringComparer.OrdinalIgnoreCase) @@ -410,6 +419,8 @@ private void parseSettingsHashtable(Hashtable settings) var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var rule in ruleArgs.Keys) { + // TODO Validate that no key is null. (Strings.KeyNotString) + var argsDict = ruleArgs[rule] as Dictionary; if (argsDict == null) { @@ -428,7 +439,7 @@ private void parseSettingsHashtable(Hashtable settings) string.Format( CultureInfo.CurrentCulture, Strings.WrongKeyHashTable, - key)); + settingKey)); } } } diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 3b71995c2..cabbf1d93 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -462,6 +462,15 @@ internal static string RulesSettingShouldBeDictionary { } } + /// + /// Looks up a localized string similar to Dictionary value for key Rules should not have any null keys. + /// + internal static string RulesSettingKeysShouldBeNonNull { + get { + return ResourceManager.GetString("RulesSettingKeysShouldBeNonNull", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary value for key Rules should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 61a66c5b4..189764e10 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -252,6 +252,9 @@ Value for key Rules should be a dictionary type. + + Dictionary value for key Rules should not have any null keys. + Dictionary value for key Rules should be indexable in a case-insensitive manner. From 0193e28a53022b49f130d2c0ba47f28ae2eadd1a Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 03:31:28 -0500 Subject: [PATCH 20/75] Validate that each key of the "Rules" setting is a string --- Engine/Settings.cs | 12 +++++++++++- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 8017e7ccc..750e5066c 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -386,7 +386,6 @@ private void parseSettingsHashtable(Hashtable settings) break; case "rules": - // TODO Validate that every key is a string. (Strings.KeyNotString) // TODO Validate that no value is null. (Strings.WrongValueHashTable) // TODO Validate that no two keys are case-insensitive duplicates. @@ -408,6 +407,16 @@ private void parseSettingsHashtable(Hashtable settings) } } + foreach (var ruleKey in rules.Keys) + { + if (!(ruleKey is string)) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.RulesSettingKeysShouldBeStrings)); + } + } + var ruleArgs = rules as Dictionary; if (ruleArgs.Comparer != StringComparer.OrdinalIgnoreCase) { @@ -420,6 +429,7 @@ private void parseSettingsHashtable(Hashtable settings) foreach (var rule in ruleArgs.Keys) { // TODO Validate that no key is null. (Strings.KeyNotString) + // TODO Validate that each key is of type string. var argsDict = ruleArgs[rule] as Dictionary; if (argsDict == null) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index cabbf1d93..4b43e9364 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -471,6 +471,15 @@ internal static string RulesSettingKeysShouldBeNonNull { } } + /// + /// Looks up a localized string similar to Dictionary value for key Rules should have only string type keys. + /// + internal static string RulesSettingKeysShouldBeStrings { + get { + return ResourceManager.GetString("RulesSettingKeysShouldBeStrings", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary value for key Rules should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 189764e10..61edfb7e8 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -255,6 +255,9 @@ Dictionary value for key Rules should not have any null keys. + + Dictionary value for key Rules should have only string type keys. + Dictionary value for key Rules should be indexable in a case-insensitive manner. From 74df2e1bb2d6fa4bf409d51cbd8d6c445789ab16 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 03:38:30 -0500 Subject: [PATCH 21/75] Validate that each key of the "Rules" setting is unique, ignoring case --- Engine/Settings.cs | 25 ++++++++++++++----------- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 750e5066c..455a18da4 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -387,7 +387,6 @@ private void parseSettingsHashtable(Hashtable settings) case "rules": // TODO Validate that no value is null. (Strings.WrongValueHashTable) - // TODO Validate that no two keys are case-insensitive duplicates. var rules = settingValue as Hashtable; if (rules == null) @@ -416,29 +415,33 @@ private void parseSettingsHashtable(Hashtable settings) Strings.RulesSettingKeysShouldBeStrings)); } } - - var ruleArgs = rules as Dictionary; - if (ruleArgs.Comparer != StringComparer.OrdinalIgnoreCase) + + var uniqueRuleKeys = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var ruleKey in rules.Keys) { - throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.RulesSettingDictionaryShouldBeCaseInsensitive)); + if (!uniqueRuleKeys.Add(ruleKey as string)) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.RuleSettingKeysShouldBeUniqueIgnoringCase)); + } } - + var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); - foreach (var rule in ruleArgs.Keys) + foreach (var ruleKey in rules.Keys.Cast()) { // TODO Validate that no key is null. (Strings.KeyNotString) // TODO Validate that each key is of type string. + // TODO Validate that no two keys are case-insensitive duplicates. - var argsDict = ruleArgs[rule] as Dictionary; + var argsDict = rules[ruleKey] as Dictionary; if (argsDict == null) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, Strings.RulesSettingValuesShouldBeDictionaries)); } - ruleArgsDict[rule] = argsDict; + ruleArgsDict[ruleKey] = argsDict; } this.ruleArguments = ruleArgsDict; diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 4b43e9364..51c2e7f5f 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -480,6 +480,15 @@ internal static string RulesSettingKeysShouldBeStrings { } } + /// + /// Looks up a localized string similar to Dictionary value for key Rules should have unique keys, ignoring case. + /// + internal static string RuleSettingKeysShouldBeUniqueIgnoringCase { + get { + return ResourceManager.GetString("RuleSettingKeysShouldBeUniqueIgnoringCase", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary value for key Rules should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 61edfb7e8..396a771ac 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -258,6 +258,9 @@ Dictionary value for key Rules should have only string type keys. + + Dictionary value for key Rules should have unique keys, ignoring case. + Dictionary value for key Rules should be indexable in a case-insensitive manner. From 5baba1efb71553ae2a69d20316019995c443a755 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 03:44:12 -0500 Subject: [PATCH 22/75] Validate that each value of the "Rules" setting is nonnull --- Engine/Settings.cs | 13 +++++++++++-- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 455a18da4..090b40dc4 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -386,8 +386,6 @@ private void parseSettingsHashtable(Hashtable settings) break; case "rules": - // TODO Validate that no value is null. (Strings.WrongValueHashTable) - var rules = settingValue as Hashtable; if (rules == null) { @@ -426,6 +424,16 @@ private void parseSettingsHashtable(Hashtable settings) Strings.RuleSettingKeysShouldBeUniqueIgnoringCase)); } } + + foreach (var ruleValue in rules.Values) + { + if (ruleValue is null) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.RuleSettingValuesShouldBeNonNull)); + } + } var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var ruleKey in rules.Keys.Cast()) @@ -433,6 +441,7 @@ private void parseSettingsHashtable(Hashtable settings) // TODO Validate that no key is null. (Strings.KeyNotString) // TODO Validate that each key is of type string. // TODO Validate that no two keys are case-insensitive duplicates. + // TODO Validate that no value is null. (Strings.WrongValueHashTable) var argsDict = rules[ruleKey] as Dictionary; if (argsDict == null) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 51c2e7f5f..64ca8bf63 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -489,6 +489,15 @@ internal static string RuleSettingKeysShouldBeUniqueIgnoringCase { } } + /// + /// Looks up a localized string similar to Dictionary value for key Rules should not have any null values. + /// + internal static string RuleSettingValuesShouldBeNonNull { + get { + return ResourceManager.GetString("RuleSettingValuesShouldBeNonNull", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary value for key Rules should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 396a771ac..2873ce48c 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -261,6 +261,9 @@ Dictionary value for key Rules should have unique keys, ignoring case. + + Dictionary value for key Rules should not have any null values. + Dictionary value for key Rules should be indexable in a case-insensitive manner. From b495b244fb6da84dd1958f1aef2d4e066c74068b Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 05:05:13 -0500 Subject: [PATCH 23/75] Refactor validation that each value of the "Rules" setting is a dictionary type --- Engine/Settings.cs | 11 +++++++++++ Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 23 insertions(+) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 090b40dc4..c307a1b22 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -434,6 +434,17 @@ private void parseSettingsHashtable(Hashtable settings) Strings.RuleSettingValuesShouldBeNonNull)); } } + + foreach (DictionaryEntry rule in rules) + { + if (!(rule.Value is System.Collections.IDictionary)) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.RuleSettingValueForKeyShouldBeDictionary, + rule.Key)); + } + } var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var ruleKey in rules.Keys.Cast()) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 64ca8bf63..4176acfdf 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -498,6 +498,15 @@ internal static string RuleSettingValuesShouldBeNonNull { } } + /// + /// Looks up a localized string similar to Dictionary value for Rules setting key {0} should be a dictionary type. + /// + internal static string RuleSettingValueForKeyShouldBeDictionary { + get { + return ResourceManager.GetString("RuleSettingValueForKeyShouldBeDictionary", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary value for key Rules should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 2873ce48c..2b087e411 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -264,6 +264,9 @@ Dictionary value for key Rules should not have any null values. + + Dictionary value for Rules setting key {0} should be a dictionary type. + Dictionary value for key Rules should be indexable in a case-insensitive manner. From f55735f1ff9332532ff34beea6958bcfd8dd3e24 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 08:02:03 -0500 Subject: [PATCH 24/75] Validate that each setting rule argument key is nonnull --- Engine/Settings.cs | 20 +++++++++++++++++--- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index c307a1b22..987751b98 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -447,9 +447,22 @@ private void parseSettingsHashtable(Hashtable settings) } var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); - foreach (var ruleKey in rules.Keys.Cast()) + foreach (DictionaryEntry rule in rules) { - // TODO Validate that no key is null. (Strings.KeyNotString) + string ruleKey = rule.Key as string; + Hashtable ruleArgs = rule.Value as Hashtable; + + foreach (DictionaryEntry ruleArg in ruleArgs) + { + if (ruleArg.Key is null) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.SettingRuleArgumentKeyShouldBeNonNull, + ruleKey)); + } + } + // TODO Validate that each key is of type string. // TODO Validate that no two keys are case-insensitive duplicates. // TODO Validate that no value is null. (Strings.WrongValueHashTable) @@ -461,7 +474,8 @@ private void parseSettingsHashtable(Hashtable settings) CultureInfo.CurrentCulture, Strings.RulesSettingValuesShouldBeDictionaries)); } - ruleArgsDict[ruleKey] = argsDict; + + // TODO Add ruleKey and ruleValue (converted) to ruleArgsDict. } this.ruleArguments = ruleArgsDict; diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 4176acfdf..9c6e300a9 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -525,6 +525,15 @@ internal static string RulesSettingValuesShouldBeDictionaries { } } + /// + /// Looks up a localized string similar to Setting rule '{0}' argument key should be nonnull. + /// + internal static string SettingRuleArgumentKeyShouldBeNonNull { + get { + return ResourceManager.GetString("SettingRuleArgumentKeyShouldBeNonNull", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 2b087e411..eea415322 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -273,6 +273,9 @@ Dictionary values for key Rules should be dictionary types. + + Setting rule '{0}' argument key should be nonnull. + Dictionary should be indexable in a case-insensitive manner. From 062bbd3cc2ff9e8d8c5928cf8f320b2eabb29df9 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 08:06:30 -0500 Subject: [PATCH 25/75] Validate that each setting rule argument key is of type string --- Engine/Settings.cs | 13 ++++++++++++- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 987751b98..5bc54f912 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -463,7 +463,18 @@ private void parseSettingsHashtable(Hashtable settings) } } - // TODO Validate that each key is of type string. + foreach (DictionaryEntry ruleArg in ruleArgs) + { + if (!(ruleArg.Key is string)) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.SettingRuleArgumentKeyShouldBeStringType, + ruleKey, + ruleArg.Key)); + } + } + // TODO Validate that no two keys are case-insensitive duplicates. // TODO Validate that no value is null. (Strings.WrongValueHashTable) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 9c6e300a9..58fc4a507 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -534,6 +534,15 @@ internal static string SettingRuleArgumentKeyShouldBeNonNull { } } + /// + /// Looks up a localized string similar to Setting rule '{0}' argument key '{1}' should be string type. + /// + internal static string SettingRuleArgumentKeyShouldBeStringType { + get { + return ResourceManager.GetString("SettingRuleArgumentKeyShouldBeStringType", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index eea415322..fd48b1664 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -276,6 +276,9 @@ Setting rule '{0}' argument key should be nonnull. + + Setting rule '{0}' argument key '{1}' should be string type. + Dictionary should be indexable in a case-insensitive manner. From 051f3450fada7d6e1d7194d019faf614d332731b Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 08:12:46 -0500 Subject: [PATCH 26/75] Validate that each setting rule argument key is unique, ignore case --- Engine/Settings.cs | 14 +++++++++++++- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 5bc54f912..7166c7ccc 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -475,7 +475,19 @@ private void parseSettingsHashtable(Hashtable settings) } } - // TODO Validate that no two keys are case-insensitive duplicates. + ISet uniqueRuleArgKeys = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (string ruleArgKey in ruleArgs.Keys.Cast()) + { + if (!uniqueRuleArgKeys.Add(ruleArgKey)) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.SettingRuleArgumentKeyShouldBeUniqueIgnoringCase, + ruleKey, + ruleArgKey)); + } + } + // TODO Validate that no value is null. (Strings.WrongValueHashTable) var argsDict = rules[ruleKey] as Dictionary; diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 58fc4a507..855c3faea 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -543,6 +543,15 @@ internal static string SettingRuleArgumentKeyShouldBeStringType { } } + /// + /// Looks up a localized string similar to Setting rule '{0}' argument key '{1}' should be unique, ignoring case. + /// + internal static string SettingRuleArgumentKeyShouldBeUniqueIgnoringCase { + get { + return ResourceManager.GetString("SettingRuleArgumentKeyShouldBeUniqueIgnoringCase", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index fd48b1664..14af64242 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -279,6 +279,9 @@ Setting rule '{0}' argument key '{1}' should be string type. + + Setting rule '{0}' argument key '{1}' should be unique, ignoring case. + Dictionary should be indexable in a case-insensitive manner. From ac1b17a74db36276e91ed8ccdac9af7fa78a7ec7 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 10:29:07 -0500 Subject: [PATCH 27/75] Validate that each setting rule argument value is nonnull --- Engine/Settings.cs | 13 ++++++++++++- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 7166c7ccc..95ea60514 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -488,7 +488,18 @@ private void parseSettingsHashtable(Hashtable settings) } } - // TODO Validate that no value is null. (Strings.WrongValueHashTable) + // COMBAK Permit null setting rule argument values. + foreach (DictionaryEntry ruleArg in ruleArgs) + { + if (ruleArg.Value is null) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.SettingRuleArgumentValueShouldBeNonNull, + ruleKey, + ruleArg.Key)); + } + } var argsDict = rules[ruleKey] as Dictionary; if (argsDict == null) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 855c3faea..b0f976586 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -552,6 +552,15 @@ internal static string SettingRuleArgumentKeyShouldBeUniqueIgnoringCase { } } + /// + /// Looks up a localized string similar to Setting rule '{0}' argument value for key '{1}' should be nonnull. + /// + internal static string SettingRuleArgumentValueShouldBeNonNull { + get { + return ResourceManager.GetString("SettingRuleArgumentValueShouldBeNonNull", resourceCulture); + } + } + /// /// Looks up a localized string similar to Dictionary should be indexable in a case-insensitive manner. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 14af64242..9d7b4c793 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -282,6 +282,9 @@ Setting rule '{0}' argument key '{1}' should be unique, ignoring case. + + Setting rule '{0}' argument value for key '{1}' should be nonnull. + Dictionary should be indexable in a case-insensitive manner. From 1cfaa676f88a163ed8dbcf886914dd86d7db127c Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 10:30:32 -0500 Subject: [PATCH 28/75] Assign parse of settings "Rules" to Settings.ruleArguments --- Engine/Settings.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 95ea60514..66fc0f5c6 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -316,7 +316,6 @@ private List GetData(object val, string key) private void parseSettingsHashtable(Hashtable settings) { - // TODO Recurse on hashtable values. ISet uniqueKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry setting in settings) { @@ -446,7 +445,7 @@ private void parseSettingsHashtable(Hashtable settings) } } - var ruleArgsDict = new Dictionary>(StringComparer.OrdinalIgnoreCase); + var typedRules = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry rule in rules) { string ruleKey = rule.Key as string; @@ -501,18 +500,15 @@ private void parseSettingsHashtable(Hashtable settings) } } - var argsDict = rules[ruleKey] as Dictionary; - if (argsDict == null) + var typedArguments = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (DictionaryEntry ruleArg in ruleArgs) { - throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.RulesSettingValuesShouldBeDictionaries)); + typedArguments[ruleArg.Key as string] = ruleArg.Value; } - - // TODO Add ruleKey and ruleValue (converted) to ruleArgsDict. + typedRules[ruleKey] = typedArguments; } - this.ruleArguments = ruleArgsDict; + this.ruleArguments = typedRules; break; default: From 98ec8f2a93544cb234963c98cd134375c0edaacf Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 10:53:17 -0500 Subject: [PATCH 29/75] Validate (explicitly) that each setting key is nonnnull --- Engine/Settings.cs | 9 +++++++-- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 66fc0f5c6..4102efeff 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -316,9 +316,14 @@ private List GetData(object val, string key) private void parseSettingsHashtable(Hashtable settings) { - ISet uniqueKeys = new HashSet(StringComparer.OrdinalIgnoreCase); + ISet uniqueSettingKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry setting in settings) { + if (setting.Key is null) + { + throw new InvalidDataException(Strings.SettingKeyIsNull); + } + if (!(setting.Key is string)) { throw new InvalidDataException( @@ -329,7 +334,7 @@ private void parseSettingsHashtable(Hashtable settings) } string settingKey = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 - if (!uniqueKeys.Add(settingKey)) + if (!uniqueSettingKeys.Add(settingKey)) { throw new InvalidDataException( string.Format( diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index b0f976586..37fcff358 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -453,6 +453,15 @@ internal static string SettingsInputShouldBeDictionary { } } + /// + /// Looks up a localized string similar to A setting key is null. + /// + internal static string SettingKeyIsNull { + get { + return ResourceManager.GetString("SettingKeyIsNull", resourceCulture); + } + } + /// /// Looks up a localized string similar to Value for key Rules should be a dictionary type. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 9d7b4c793..77a3509f7 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -249,6 +249,9 @@ Input should be a dictionary type. + + A setting key is null. + Value for key Rules should be a dictionary type. From 5a657439c356defa3a272990bdde06bfafa6746b Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 10:58:02 -0500 Subject: [PATCH 30/75] Clean up validation that each setting key is a string type --- Engine/Settings.cs | 28 +++++++++++++--------------- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 4102efeff..ad0a0657d 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -326,21 +326,19 @@ private void parseSettingsHashtable(Hashtable settings) if (!(setting.Key is string)) { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.KeyNotString, - setting.Key)); + throw new InvalidDataException(string.Format( + Strings.SettingKeyIsNotStringType, + setting.Key)); } - string settingKey = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 + string typedSettingKey = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 - if (!uniqueSettingKeys.Add(settingKey)) + if (!uniqueSettingKeys.Add(typedSettingKey)) { throw new InvalidDataException( string.Format( CultureInfo.CurrentCulture, Strings.KeyNotUniqueIgnoringCase, - settingKey)); + typedSettingKey)); } if (setting.Value is null) @@ -354,22 +352,22 @@ private void parseSettingsHashtable(Hashtable settings) } object settingValue = setting.Value; - switch (settingKey) + switch (typedSettingKey) { case "severity": - severities = GetData(settingValue, settingKey); + severities = GetData(settingValue, typedSettingKey); break; case "includerules": - includeRules = GetData(settingValue, settingKey); + includeRules = GetData(settingValue, typedSettingKey); break; case "excluderules": - excludeRules = GetData(settingValue, settingKey); + excludeRules = GetData(settingValue, typedSettingKey); break; case "customrulepath": - customRulePath = GetData(settingValue, settingKey); + customRulePath = GetData(settingValue, typedSettingKey); break; case "includedefaultrules": @@ -384,7 +382,7 @@ private void parseSettingsHashtable(Hashtable settings) var booleanVal = (bool)settingValue; var field = this.GetType().GetField( - settingKey, + typedSettingKey, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.NonPublic); field.SetValue(this, booleanVal); break; @@ -521,7 +519,7 @@ private void parseSettingsHashtable(Hashtable settings) string.Format( CultureInfo.CurrentCulture, Strings.WrongKeyHashTable, - settingKey)); + typedSettingKey)); } } } diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 37fcff358..9da48a873 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -462,6 +462,15 @@ internal static string SettingKeyIsNull { } } + /// + /// Looks up a localized string similar to The setting key '{0}' is not a string type. + /// + internal static string SettingKeyIsNotStringType { + get { + return ResourceManager.GetString("SettingKeyIsNotStringType", resourceCulture); + } + } + /// /// Looks up a localized string similar to Value for key Rules should be a dictionary type. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 77a3509f7..8d7b9796a 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -252,6 +252,9 @@ A setting key is null. + + The setting key '{0}' is not a string type. + Value for key Rules should be a dictionary type. From 7862461210d9ebe43562e878056e2d754af5d793 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 11:05:02 -0500 Subject: [PATCH 31/75] Clean up validation that each setting key is unique, ignoring case --- Engine/Settings.cs | 9 ++++----- Engine/Strings.Designer.cs | 18 +++++++++--------- Engine/Strings.resx | 6 +++--- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index ad0a0657d..4c69cc687 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -334,11 +334,10 @@ private void parseSettingsHashtable(Hashtable settings) if (!uniqueSettingKeys.Add(typedSettingKey)) { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.KeyNotUniqueIgnoringCase, - typedSettingKey)); + // setting.Key should be used instead of typedSettingKey because the former preserves information about the source casing. + throw new InvalidDataException(string.Format( + Strings.SettingKeyIsNotUniqueIgnoringCase, + setting.Key)); } if (setting.Value is null) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 9da48a873..12a44b5e5 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -417,15 +417,6 @@ internal static string KeyNotString { } } - /// - /// Looks up a localized string similar to Key {0} in the settings is not unique, ignore casing. - /// - internal static string KeyNotUniqueIgnoringCase { - get { - return ResourceManager.GetString("KeyNotUniqueIgnoringCase", resourceCulture); - } - } - /// /// Looks up a localized string similar to {0} is not a valid key in the settings hashtable. Valid keys are ExcludeRules, IncludeRules and Severity. /// @@ -471,6 +462,15 @@ internal static string SettingKeyIsNotStringType { } } + /// + /// Looks up a localized string similar to The setting key '{0}' is not unique, ignoring case. + /// + internal static string SettingKeyIsNotUniqueIgnoringCase { + get { + return ResourceManager.GetString("SettingKeyIsNotUniqueIgnoringCase", resourceCulture); + } + } + /// /// Looks up a localized string similar to Value for key Rules should be a dictionary type. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 8d7b9796a..e27d1e0bd 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -237,9 +237,6 @@ Key {0} in the settings is not a string. - - Key {0} in the settings is not unique, ignore casing. - {0} is not a valid key in the settings hashtable. Valid keys are ExcludeRules, IncludeRules and Severity. @@ -255,6 +252,9 @@ The setting key '{0}' is not a string type. + + The setting key '{0}' is not unique, ignoring case. + Value for key Rules should be a dictionary type. From 526ed3af157fe0fc2c51df93be7973c72be8c668 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 11:13:43 -0500 Subject: [PATCH 32/75] Clean up validation that each setting value is nonnull --- Engine/Settings.cs | 24 ++++++++++-------------- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 4c69cc687..9794d5628 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -342,36 +342,32 @@ private void parseSettingsHashtable(Hashtable settings) if (setting.Value is null) { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.WrongValueHashTable, - setting.Value, - setting.Key)); + throw new InvalidDataException(string.Format( + Strings.SettingValueIsNull, + typedSettingKey)); } - object settingValue = setting.Value; switch (typedSettingKey) { case "severity": - severities = GetData(settingValue, typedSettingKey); + severities = GetData(setting.Value, typedSettingKey); break; case "includerules": - includeRules = GetData(settingValue, typedSettingKey); + includeRules = GetData(setting.Value, typedSettingKey); break; case "excluderules": - excludeRules = GetData(settingValue, typedSettingKey); + excludeRules = GetData(setting.Value, typedSettingKey); break; case "customrulepath": - customRulePath = GetData(settingValue, typedSettingKey); + customRulePath = GetData(setting.Value, typedSettingKey); break; case "includedefaultrules": case "recursecustomrulepath": - if (!(settingValue is bool)) + if (!(setting.Value is bool)) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, @@ -379,7 +375,7 @@ private void parseSettingsHashtable(Hashtable settings) setting.Key)); } - var booleanVal = (bool)settingValue; + var booleanVal = (bool)setting.Value; var field = this.GetType().GetField( typedSettingKey, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.NonPublic); @@ -387,7 +383,7 @@ private void parseSettingsHashtable(Hashtable settings) break; case "rules": - var rules = settingValue as Hashtable; + var rules = setting.Value as Hashtable; if (rules == null) { throw new InvalidDataException(string.Format( diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 12a44b5e5..c44c0d9c0 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -471,6 +471,15 @@ internal static string SettingKeyIsNotUniqueIgnoringCase { } } + /// + /// Looks up a localized string similar to The value for the '{0}' setting is null. + /// + internal static string SettingValueIsNull { + get { + return ResourceManager.GetString("SettingValueIsNull", resourceCulture); + } + } + /// /// Looks up a localized string similar to Value for key Rules should be a dictionary type. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index e27d1e0bd..d402e09ae 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -255,6 +255,9 @@ The setting key '{0}' is not unique, ignoring case. + + The value for the '{0}' setting is null. + Value for key Rules should be a dictionary type. From ccbeccd45ce7325a492a52f56471c38a11fb0dff Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 11:20:49 -0500 Subject: [PATCH 33/75] Clean up validation that the value for the 'Rules' setting is a dictionary type --- Engine/Settings.cs | 8 ++++---- Engine/Strings.Designer.cs | 6 +++--- Engine/Strings.resx | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 9794d5628..e9941c34e 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -383,13 +383,13 @@ private void parseSettingsHashtable(Hashtable settings) break; case "rules": - var rules = setting.Value as Hashtable; - if (rules == null) + if (!(setting.Value is System.Collections.IDictionary)) { throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.RulesSettingShouldBeDictionary)); + Strings.SettingRulesValueIsNotDictionaryType, + setting.Value)); } + Hashtable rules = setting.Value as Hashtable; foreach (var ruleKey in rules.Keys) { diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index c44c0d9c0..aabac5079 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -481,11 +481,11 @@ internal static string SettingValueIsNull { } /// - /// Looks up a localized string similar to Value for key Rules should be a dictionary type. + /// Looks up a localized string similar to The value for the 'Rules' setting is not a dictionary type. /// - internal static string RulesSettingShouldBeDictionary { + internal static string SettingRulesValueIsNotDictionaryType { get { - return ResourceManager.GetString("RulesSettingShouldBeDictionary", resourceCulture); + return ResourceManager.GetString("SettingRulesValueIsNotDictionaryType", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index d402e09ae..9c6543482 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -258,8 +258,8 @@ The value for the '{0}' setting is null. - - Value for key Rules should be a dictionary type. + + The value for the 'Rules' setting is not a dictionary type. Dictionary value for key Rules should not have any null keys. From 5c56c8518a065703509e0d22478ec223b3582e67 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 11:29:11 -0500 Subject: [PATCH 34/75] Clean up validation that each 'Rules' setting rule key is nonnull --- Engine/Settings.cs | 11 ++++++----- Engine/Strings.Designer.cs | 8 ++++---- Engine/Strings.resx | 6 +++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index e9941c34e..79488119f 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -391,15 +391,16 @@ private void parseSettingsHashtable(Hashtable settings) } Hashtable rules = setting.Value as Hashtable; - foreach (var ruleKey in rules.Keys) + foreach (DictionaryEntry rule in rules) { - if (ruleKey is null) + if (rule.Key is null) { - throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.RulesSettingKeysShouldBeNonNull)); + throw new InvalidDataException(Strings.SettingRuleKeyIsNull); } + + // TODO Refactor successor loops into nested loops. } + foreach (var ruleKey in rules.Keys) { diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index aabac5079..431c983f2 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -445,7 +445,7 @@ internal static string SettingsInputShouldBeDictionary { } /// - /// Looks up a localized string similar to A setting key is null. + /// Looks up a localized string similar to The key for a setting is null. /// internal static string SettingKeyIsNull { get { @@ -490,11 +490,11 @@ internal static string SettingRulesValueIsNotDictionaryType { } /// - /// Looks up a localized string similar to Dictionary value for key Rules should not have any null keys. + /// Looks up a localized string similar to The key for a 'Rules' setting rule is null. /// - internal static string RulesSettingKeysShouldBeNonNull { + internal static string SettingRuleKeyIsNull { get { - return ResourceManager.GetString("RulesSettingKeysShouldBeNonNull", resourceCulture); + return ResourceManager.GetString("SettingRuleKeyIsNull", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 9c6543482..4fac17218 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -247,7 +247,7 @@ Input should be a dictionary type. - A setting key is null. + The key for a setting is null. The setting key '{0}' is not a string type. @@ -261,8 +261,8 @@ The value for the 'Rules' setting is not a dictionary type. - - Dictionary value for key Rules should not have any null keys. + + The key for a 'Rules' setting rule is null. Dictionary value for key Rules should have only string type keys. From 1adb84a8c8f5f8dae38d32c927f09f9aa601d9fd Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 11:34:06 -0500 Subject: [PATCH 35/75] Clean up validation that each setting rule key is a string type --- Engine/Settings.cs | 37 ++++++++++++++++++------------------- Engine/Strings.Designer.cs | 6 +++--- Engine/Strings.resx | 4 ++-- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 79488119f..e2f4b6c44 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -330,9 +330,9 @@ private void parseSettingsHashtable(Hashtable settings) Strings.SettingKeyIsNotStringType, setting.Key)); } - string typedSettingKey = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 + string settingName = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 - if (!uniqueSettingKeys.Add(typedSettingKey)) + if (!uniqueSettingKeys.Add(settingName)) { // setting.Key should be used instead of typedSettingKey because the former preserves information about the source casing. throw new InvalidDataException(string.Format( @@ -344,25 +344,25 @@ private void parseSettingsHashtable(Hashtable settings) { throw new InvalidDataException(string.Format( Strings.SettingValueIsNull, - typedSettingKey)); + settingName)); } - switch (typedSettingKey) + switch (settingName) { case "severity": - severities = GetData(setting.Value, typedSettingKey); + severities = GetData(setting.Value, settingName); break; case "includerules": - includeRules = GetData(setting.Value, typedSettingKey); + includeRules = GetData(setting.Value, settingName); break; case "excluderules": - excludeRules = GetData(setting.Value, typedSettingKey); + excludeRules = GetData(setting.Value, settingName); break; case "customrulepath": - customRulePath = GetData(setting.Value, typedSettingKey); + customRulePath = GetData(setting.Value, settingName); break; case "includedefaultrules": @@ -377,7 +377,7 @@ private void parseSettingsHashtable(Hashtable settings) var booleanVal = (bool)setting.Value; var field = this.GetType().GetField( - typedSettingKey, + settingName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.NonPublic); field.SetValue(this, booleanVal); break; @@ -398,20 +398,19 @@ private void parseSettingsHashtable(Hashtable settings) throw new InvalidDataException(Strings.SettingRuleKeyIsNull); } - // TODO Refactor successor loops into nested loops. - } - - - foreach (var ruleKey in rules.Keys) - { - if (!(ruleKey is string)) + if (!(rule.Key is string)) { throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.RulesSettingKeysShouldBeStrings)); + Strings.SettingRuleKeyIsNotStringType, + rule.Key)); } + string ruleName = rule.Key as string; + + // TODO Refactor successor loops into nested loops. } + + var uniqueRuleKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var ruleKey in rules.Keys) { @@ -515,7 +514,7 @@ private void parseSettingsHashtable(Hashtable settings) string.Format( CultureInfo.CurrentCulture, Strings.WrongKeyHashTable, - typedSettingKey)); + settingName)); } } } diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 431c983f2..efad0e90c 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -499,11 +499,11 @@ internal static string SettingRuleKeyIsNull { } /// - /// Looks up a localized string similar to Dictionary value for key Rules should have only string type keys. + /// Looks up a localized string similar to The key for the 'Rules' setting '{0}' rule is not a string type. /// - internal static string RulesSettingKeysShouldBeStrings { + internal static string SettingRuleKeyIsNotStringType { get { - return ResourceManager.GetString("RulesSettingKeysShouldBeStrings", resourceCulture); + return ResourceManager.GetString("SettingRuleKeyIsNotStringType", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 4fac17218..27a6e0e3a 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -264,8 +264,8 @@ The key for a 'Rules' setting rule is null. - - Dictionary value for key Rules should have only string type keys. + + The key for the 'Rules' setting '{0}' rule is not a string type. Dictionary value for key Rules should have unique keys, ignoring case. From feb040975b3653ff74acd4194f5398a874ce62ba Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 11:39:07 -0500 Subject: [PATCH 36/75] Clean up validation that each 'Rules' setting key is unique, ignoring case --- Engine/Settings.cs | 20 ++++++++------------ Engine/Strings.Designer.cs | 6 +++--- Engine/Strings.resx | 4 ++-- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index e2f4b6c44..aa580591e 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -334,7 +334,7 @@ private void parseSettingsHashtable(Hashtable settings) if (!uniqueSettingKeys.Add(settingName)) { - // setting.Key should be used instead of typedSettingKey because the former preserves information about the source casing. + // setting.Key should be used instead of settingName because the former preserves information about the source casing. throw new InvalidDataException(string.Format( Strings.SettingKeyIsNotUniqueIgnoringCase, setting.Key)); @@ -391,6 +391,7 @@ private void parseSettingsHashtable(Hashtable settings) } Hashtable rules = setting.Value as Hashtable; + ISet uniqueRuleKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry rule in rules) { if (rule.Key is null) @@ -406,20 +407,15 @@ private void parseSettingsHashtable(Hashtable settings) } string ruleName = rule.Key as string; - // TODO Refactor successor loops into nested loops. - } - - - - var uniqueRuleKeys = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var ruleKey in rules.Keys) - { - if (!uniqueRuleKeys.Add(ruleKey as string)) + if (!uniqueRuleKeys.Add(ruleName)) { + // rule.Key should be used instead of ruleName because the former preserves information about the source casing. throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.RuleSettingKeysShouldBeUniqueIgnoringCase)); + Strings.SettingRuleKeyIsNotUniqueIgnoringCase, + rule.Key)); } + + // TODO Refactor successor loops into nested loops. } foreach (var ruleValue in rules.Values) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index efad0e90c..079c2e7b8 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -508,11 +508,11 @@ internal static string SettingRuleKeyIsNotStringType { } /// - /// Looks up a localized string similar to Dictionary value for key Rules should have unique keys, ignoring case. + /// Looks up a localized string similar to The key for the 'Rules' setting '{0}' rule is not unique, ignoring case. /// - internal static string RuleSettingKeysShouldBeUniqueIgnoringCase { + internal static string SettingRuleKeyIsNotUniqueIgnoringCase { get { - return ResourceManager.GetString("RuleSettingKeysShouldBeUniqueIgnoringCase", resourceCulture); + return ResourceManager.GetString("SettingRuleKeyIsNotUniqueIgnoringCase", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 27a6e0e3a..9e7f9e8b3 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -267,8 +267,8 @@ The key for the 'Rules' setting '{0}' rule is not a string type. - - Dictionary value for key Rules should have unique keys, ignoring case. + + The key for the 'Rules' setting '{0}' rule is not unique, ignoring case. Dictionary value for key Rules should not have any null values. From 5a721ba45d6f8a1183b47dfb0159e867d6d8e78b Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 11:42:21 -0500 Subject: [PATCH 37/75] Clean up validation that each 'Rules' setting rule value is nonnull --- Engine/Settings.cs | 14 ++++++-------- Engine/Strings.Designer.cs | 6 +++--- Engine/Strings.resx | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index aa580591e..00396e6fc 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -415,19 +415,17 @@ private void parseSettingsHashtable(Hashtable settings) rule.Key)); } - // TODO Refactor successor loops into nested loops. - } - - foreach (var ruleValue in rules.Values) - { - if (ruleValue is null) + if (rule.Value is null) { throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.RuleSettingValuesShouldBeNonNull)); + Strings.SettingRuleValueIsNull, + ruleName)); } + + // TODO Refactor successor loops into nested loops. } + foreach (DictionaryEntry rule in rules) { if (!(rule.Value is System.Collections.IDictionary)) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 079c2e7b8..32eb83a45 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -517,11 +517,11 @@ internal static string SettingRuleKeyIsNotUniqueIgnoringCase { } /// - /// Looks up a localized string similar to Dictionary value for key Rules should not have any null values. + /// Looks up a localized string similar to The value for the 'Rules' setting '{0}' rule is null. /// - internal static string RuleSettingValuesShouldBeNonNull { + internal static string SettingRuleValueIsNull { get { - return ResourceManager.GetString("RuleSettingValuesShouldBeNonNull", resourceCulture); + return ResourceManager.GetString("SettingRuleValueIsNull", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 9e7f9e8b3..60f783c10 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -270,8 +270,8 @@ The key for the 'Rules' setting '{0}' rule is not unique, ignoring case. - - Dictionary value for key Rules should not have any null values. + + The value for the 'Rules' setting '{0}' rule is null. Dictionary value for Rules setting key {0} should be a dictionary type. From 0f0f4c6e1797882e6727590676cc9921fad95d6f Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 11:45:42 -0500 Subject: [PATCH 38/75] Clean up validation that each 'Rules' setting rule value is a dictionary type --- Engine/Settings.cs | 17 ++++++++--------- Engine/Strings.Designer.cs | 6 +++--- Engine/Strings.resx | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 00396e6fc..cfa201046 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -422,20 +422,19 @@ private void parseSettingsHashtable(Hashtable settings) ruleName)); } - // TODO Refactor successor loops into nested loops. - } - - - foreach (DictionaryEntry rule in rules) - { if (!(rule.Value is System.Collections.IDictionary)) { throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.RuleSettingValueForKeyShouldBeDictionary, - rule.Key)); + Strings.SettingRuleValueIsNotDictionaryType, + ruleName, + rule.Value)); } + Hashtable arguments = rule.Value as Hashtable; + + // TODO Refactor successor loops into nested loops. } + + var typedRules = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry rule in rules) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 32eb83a45..a41412940 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -526,11 +526,11 @@ internal static string SettingRuleValueIsNull { } /// - /// Looks up a localized string similar to Dictionary value for Rules setting key {0} should be a dictionary type. + /// Looks up a localized string similar to The value '{1}' for the 'Rules' setting '{0}' rule is not a dictionary type. /// - internal static string RuleSettingValueForKeyShouldBeDictionary { + internal static string SettingRuleValueIsNotDictionaryType { get { - return ResourceManager.GetString("RuleSettingValueForKeyShouldBeDictionary", resourceCulture); + return ResourceManager.GetString("SettingRuleValueIsNotDictionaryType", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 60f783c10..f8329bb01 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -273,8 +273,8 @@ The value for the 'Rules' setting '{0}' rule is null. - - Dictionary value for Rules setting key {0} should be a dictionary type. + + The value '{1}' for the 'Rules' setting '{0}' rule is not a dictionary type. Dictionary value for key Rules should be indexable in a case-insensitive manner. From 8dd535076eeed9ec1d6c00475241c7362172198d Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 11:53:58 -0500 Subject: [PATCH 39/75] Refactor the new 'Setting*' resource strings standard format for extensibility --- Engine/Strings.resx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Engine/Strings.resx b/Engine/Strings.resx index f8329bb01..68221f6ee 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -246,8 +246,9 @@ Input should be a dictionary type. + - The key for a setting is null. + A setting key is null. The setting key '{0}' is not a string type. @@ -256,26 +257,27 @@ The setting key '{0}' is not unique, ignoring case. - The value for the '{0}' setting is null. + The setting '{0}' value is null. - The value for the 'Rules' setting is not a dictionary type. + The setting 'Rules' value '{0}' is not a dictionary type. - The key for a 'Rules' setting rule is null. + A setting 'Rules', rule key is null. - The key for the 'Rules' setting '{0}' rule is not a string type. + The setting 'Rules', rule key '{0}' is not a string type. - The key for the 'Rules' setting '{0}' rule is not unique, ignoring case. + The setting 'Rules', rule key '{0}' is not unique, ignoring case. - The value for the 'Rules' setting '{0}' rule is null. + The setting 'Rules', rule '{0}' value is null. - The value '{1}' for the 'Rules' setting '{0}' rule is not a dictionary type. + The setting 'Rules', rule '{0}' value '{1}' is not a dictionary type. + Dictionary value for key Rules should be indexable in a case-insensitive manner. From 19fc6d45b5dd3399b9021acd81f9d81052293921 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 12:08:09 -0500 Subject: [PATCH 40/75] Nest remaining successor loops for parsing 'Rules' setting --- Engine/Settings.cs | 69 +++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index cfa201046..9dc3ec0cc 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -391,6 +391,7 @@ private void parseSettingsHashtable(Hashtable settings) } Hashtable rules = setting.Value as Hashtable; + var parsedRules = new Dictionary>(StringComparer.OrdinalIgnoreCase); ISet uniqueRuleKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry rule in rules) { @@ -431,75 +432,55 @@ private void parseSettingsHashtable(Hashtable settings) } Hashtable arguments = rule.Value as Hashtable; - // TODO Refactor successor loops into nested loops. - } - - - - var typedRules = new Dictionary>(StringComparer.OrdinalIgnoreCase); - foreach (DictionaryEntry rule in rules) - { - string ruleKey = rule.Key as string; - Hashtable ruleArgs = rule.Value as Hashtable; - - foreach (DictionaryEntry ruleArg in ruleArgs) + var parsedArguments = new Dictionary(StringComparer.OrdinalIgnoreCase); + ISet uniqueArgumentKeys = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (DictionaryEntry argument in arguments) { - if (ruleArg.Key is null) + // TODO Clean up each following validating parsing step. + + if (argument.Key is null) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, Strings.SettingRuleArgumentKeyShouldBeNonNull, - ruleKey)); + ruleName)); } - } - - foreach (DictionaryEntry ruleArg in ruleArgs) - { - if (!(ruleArg.Key is string)) + + if (!(argument.Key is string)) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, Strings.SettingRuleArgumentKeyShouldBeStringType, - ruleKey, - ruleArg.Key)); + ruleName, + argument.Key)); } - } - - ISet uniqueRuleArgKeys = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (string ruleArgKey in ruleArgs.Keys.Cast()) - { - if (!uniqueRuleArgKeys.Add(ruleArgKey)) + + if (!uniqueArgumentKeys.Add(argument.Key as string)) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, Strings.SettingRuleArgumentKeyShouldBeUniqueIgnoringCase, - ruleKey, - ruleArgKey)); + ruleName, + argument.Key)); } - } - - // COMBAK Permit null setting rule argument values. - foreach (DictionaryEntry ruleArg in ruleArgs) - { - if (ruleArg.Value is null) + + // COMBAK Permit null setting rule argument values. + if (argument.Value is null) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, Strings.SettingRuleArgumentValueShouldBeNonNull, - ruleKey, - ruleArg.Key)); + ruleName, + argument.Key)); } - } - var typedArguments = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (DictionaryEntry ruleArg in ruleArgs) - { - typedArguments[ruleArg.Key as string] = ruleArg.Value; + parsedArguments[argument.Key as string] = argument.Value; } - typedRules[ruleKey] = typedArguments; + + parsedRules[ruleName] = parsedArguments; } - this.ruleArguments = typedRules; + this.ruleArguments = parsedRules; break; default: From 9ba1ee9d65b10910762d0a22950de4749809b682 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 12:11:07 -0500 Subject: [PATCH 41/75] Remove two now-unused resource strings --- Engine/Strings.Designer.cs | 34 ++++++++-------------------------- Engine/Strings.resx | 6 ------ 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index a41412940..2b4a761bd 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -445,7 +445,7 @@ internal static string SettingsInputShouldBeDictionary { } /// - /// Looks up a localized string similar to The key for a setting is null. + /// Looks up a localized string similar to A setting key is null. /// internal static string SettingKeyIsNull { get { @@ -472,7 +472,7 @@ internal static string SettingKeyIsNotUniqueIgnoringCase { } /// - /// Looks up a localized string similar to The value for the '{0}' setting is null. + /// Looks up a localized string similar to The setting '{0}' value is null. /// internal static string SettingValueIsNull { get { @@ -481,7 +481,7 @@ internal static string SettingValueIsNull { } /// - /// Looks up a localized string similar to The value for the 'Rules' setting is not a dictionary type. + /// Looks up a localized string similar to The setting 'Rules' value '{0}' is not a dictionary type. /// internal static string SettingRulesValueIsNotDictionaryType { get { @@ -490,7 +490,7 @@ internal static string SettingRulesValueIsNotDictionaryType { } /// - /// Looks up a localized string similar to The key for a 'Rules' setting rule is null. + /// Looks up a localized string similar to A setting 'Rules', rule key is null. /// internal static string SettingRuleKeyIsNull { get { @@ -499,7 +499,7 @@ internal static string SettingRuleKeyIsNull { } /// - /// Looks up a localized string similar to The key for the 'Rules' setting '{0}' rule is not a string type. + /// Looks up a localized string similar to The setting 'Rules', rule key '{0}' is not a string type. /// internal static string SettingRuleKeyIsNotStringType { get { @@ -508,7 +508,7 @@ internal static string SettingRuleKeyIsNotStringType { } /// - /// Looks up a localized string similar to The key for the 'Rules' setting '{0}' rule is not unique, ignoring case. + /// Looks up a localized string similar to The setting 'Rules', rule key '{0}' is not unique, ignoring case. /// internal static string SettingRuleKeyIsNotUniqueIgnoringCase { get { @@ -517,7 +517,7 @@ internal static string SettingRuleKeyIsNotUniqueIgnoringCase { } /// - /// Looks up a localized string similar to The value for the 'Rules' setting '{0}' rule is null. + /// Looks up a localized string similar to The setting 'Rules', rule '{0}' value is null. /// internal static string SettingRuleValueIsNull { get { @@ -526,7 +526,7 @@ internal static string SettingRuleValueIsNull { } /// - /// Looks up a localized string similar to The value '{1}' for the 'Rules' setting '{0}' rule is not a dictionary type. + /// Looks up a localized string similar to The setting 'Rules', rule '{0}' value '{1}' is not a dictionary type. /// internal static string SettingRuleValueIsNotDictionaryType { get { @@ -534,24 +534,6 @@ internal static string SettingRuleValueIsNotDictionaryType { } } - /// - /// Looks up a localized string similar to Dictionary value for key Rules should be indexable in a case-insensitive manner. - /// - internal static string RulesSettingDictionaryShouldBeCaseInsensitive { - get { - return ResourceManager.GetString("RulesSettingDictionaryShouldBeCaseInsensitive", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Dictionary values for key Rules should be dictionary types. - /// - internal static string RulesSettingValuesShouldBeDictionaries { - get { - return ResourceManager.GetString("RulesSettingValuesShouldBeDictionaries", resourceCulture); - } - } - /// /// Looks up a localized string similar to Setting rule '{0}' argument key should be nonnull. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 68221f6ee..99dfafc6f 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -278,12 +278,6 @@ The setting 'Rules', rule '{0}' value '{1}' is not a dictionary type. - - Dictionary value for key Rules should be indexable in a case-insensitive manner. - - - Dictionary values for key Rules should be dictionary types. - Setting rule '{0}' argument key should be nonnull. From b2ac862eec48a685d6bd2b03b51dca4745ed7852 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 12:13:45 -0500 Subject: [PATCH 42/75] Clean up validation that 'Rules' setting rule argument key is nonnull --- Engine/Settings.cs | 7 +++---- Engine/Strings.Designer.cs | 6 +++--- Engine/Strings.resx | 6 +++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 9dc3ec0cc..10f2e73f1 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -436,16 +436,15 @@ private void parseSettingsHashtable(Hashtable settings) ISet uniqueArgumentKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry argument in arguments) { - // TODO Clean up each following validating parsing step. - if (argument.Key is null) { throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.SettingRuleArgumentKeyShouldBeNonNull, + Strings.SettingRuleArgumentKeyIsNull, ruleName)); } + // TODO Clean up each following validating parsing steps. + if (!(argument.Key is string)) { throw new InvalidDataException(string.Format( diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 2b4a761bd..d4a79f89e 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -535,11 +535,11 @@ internal static string SettingRuleValueIsNotDictionaryType { } /// - /// Looks up a localized string similar to Setting rule '{0}' argument key should be nonnull. + /// Looks up a localized string similar to A setting 'Rules', rule '{0}', argument key is null. /// - internal static string SettingRuleArgumentKeyShouldBeNonNull { + internal static string SettingRuleArgumentKeyIsNull { get { - return ResourceManager.GetString("SettingRuleArgumentKeyShouldBeNonNull", resourceCulture); + return ResourceManager.GetString("SettingRuleArgumentKeyIsNull", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 99dfafc6f..2db7068d3 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -277,10 +277,10 @@ The setting 'Rules', rule '{0}' value '{1}' is not a dictionary type. - - - Setting rule '{0}' argument key should be nonnull. + + A setting 'Rules', rule '{0}', argument key is null. + Setting rule '{0}' argument key '{1}' should be string type. From 63b5479e7049d4015943abe222726fab36d3042e Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 12:16:31 -0500 Subject: [PATCH 43/75] Clean up validation that each 'Rules' setting rule argument key is a string type --- Engine/Settings.cs | 8 ++++---- Engine/Strings.Designer.cs | 6 +++--- Engine/Strings.resx | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 10f2e73f1..d44e963ad 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -443,16 +443,16 @@ private void parseSettingsHashtable(Hashtable settings) ruleName)); } - // TODO Clean up each following validating parsing steps. - if (!(argument.Key is string)) { throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.SettingRuleArgumentKeyShouldBeStringType, + Strings.SettingRuleArgumentKeyIsNotStringType, ruleName, argument.Key)); } + string argumentName = argument.Key as string; + + // TODO Clean up each following validating parsing steps. if (!uniqueArgumentKeys.Add(argument.Key as string)) { diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index d4a79f89e..20a95bd75 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -544,11 +544,11 @@ internal static string SettingRuleArgumentKeyIsNull { } /// - /// Looks up a localized string similar to Setting rule '{0}' argument key '{1}' should be string type. + /// Looks up a localized string similar to The setting 'Rules', rule '{0}', argument key '{1}' is not a string type. /// - internal static string SettingRuleArgumentKeyShouldBeStringType { + internal static string SettingRuleArgumentKeyIsNotStringType { get { - return ResourceManager.GetString("SettingRuleArgumentKeyShouldBeStringType", resourceCulture); + return ResourceManager.GetString("SettingRuleArgumentKeyIsNotStringType", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 2db7068d3..28819c1a5 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -280,10 +280,10 @@ A setting 'Rules', rule '{0}', argument key is null. - - - Setting rule '{0}' argument key '{1}' should be string type. + + The setting 'Rules', rule '{0}', argument key '{1}' is not a string type. + Setting rule '{0}' argument key '{1}' should be unique, ignoring case. From 07cb4e33386d66dd03e155f87350eec3e102b43f Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 12:20:41 -0500 Subject: [PATCH 44/75] Retroactively invoke String.ToLowerInvariant() on ruleName and argumentName This should've been there all along, but was overlooked due to programmer error. The reason it should be there is documented in the comment on the initialization of settingName, which refers to issue #1095. --- Engine/Settings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index d44e963ad..2d5cb97ab 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -406,7 +406,7 @@ private void parseSettingsHashtable(Hashtable settings) Strings.SettingRuleKeyIsNotStringType, rule.Key)); } - string ruleName = rule.Key as string; + string ruleName = (rule.Key as string).ToLowerInvariant(); if (!uniqueRuleKeys.Add(ruleName)) { @@ -450,11 +450,11 @@ private void parseSettingsHashtable(Hashtable settings) ruleName, argument.Key)); } - string argumentName = argument.Key as string; + string argumentName = (argument.Key as string).ToLowerInvariant(); // TODO Clean up each following validating parsing steps. - if (!uniqueArgumentKeys.Add(argument.Key as string)) + if (!uniqueArgumentKeys.Add(argumentName)) { throw new InvalidDataException(string.Format( CultureInfo.CurrentCulture, From c323a0b247c686d0c5413df6381a05dd6b1d7c2a Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 19:09:04 -0500 Subject: [PATCH 45/75] Clean up validation that each 'Rules' setting rule argument key is unique, ignoring case --- Engine/Settings.cs | 8 ++++---- Engine/Strings.Designer.cs | 6 +++--- Engine/Strings.resx | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 2d5cb97ab..d358f65b4 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -452,17 +452,17 @@ private void parseSettingsHashtable(Hashtable settings) } string argumentName = (argument.Key as string).ToLowerInvariant(); - // TODO Clean up each following validating parsing steps. - if (!uniqueArgumentKeys.Add(argumentName)) { + // argument.Key should be used instead of argumentName because the former preserves information about the source casing. throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.SettingRuleArgumentKeyShouldBeUniqueIgnoringCase, + Strings.SettingRuleArgumentKeyIsNotUniqueIgnoringCase, ruleName, argument.Key)); } + // TODO Clean up each following validating parsing steps. + // COMBAK Permit null setting rule argument values. if (argument.Value is null) { diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 20a95bd75..40adbaed6 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -553,11 +553,11 @@ internal static string SettingRuleArgumentKeyIsNotStringType { } /// - /// Looks up a localized string similar to Setting rule '{0}' argument key '{1}' should be unique, ignoring case. + /// Looks up a localized string similar to The setting 'Rules', rule '{0}', argument key '{1}' is not unique, ignoring case. /// - internal static string SettingRuleArgumentKeyShouldBeUniqueIgnoringCase { + internal static string SettingRuleArgumentKeyIsNotUniqueIgnoringCase { get { - return ResourceManager.GetString("SettingRuleArgumentKeyShouldBeUniqueIgnoringCase", resourceCulture); + return ResourceManager.GetString("SettingRuleArgumentKeyIsNotUniqueIgnoringCase", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 28819c1a5..28f52a15d 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -283,10 +283,10 @@ The setting 'Rules', rule '{0}', argument key '{1}' is not a string type. - - - Setting rule '{0}' argument key '{1}' should be unique, ignoring case. + + The setting 'Rules', rule '{0}', argument key '{1}' is not unique, ignoring case. + Setting rule '{0}' argument value for key '{1}' should be nonnull. From 12e365202fb9eea94a75c915f6168b76792b7778 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 23 Jun 2019 19:12:55 -0500 Subject: [PATCH 46/75] Clean up validation that each 'Rules' setting rule argument value is nonnull --- Engine/Settings.cs | 10 +++------- Engine/Strings.Designer.cs | 6 +++--- Engine/Strings.resx | 6 +++--- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index d358f65b4..dcda1f891 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -461,19 +461,15 @@ private void parseSettingsHashtable(Hashtable settings) argument.Key)); } - // TODO Clean up each following validating parsing steps. - - // COMBAK Permit null setting rule argument values. if (argument.Value is null) { throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.SettingRuleArgumentValueShouldBeNonNull, + Strings.SettingRuleArgumentValueIsNull, ruleName, - argument.Key)); + argumentName)); } - parsedArguments[argument.Key as string] = argument.Value; + parsedArguments[argumentName] = argument.Value; } parsedRules[ruleName] = parsedArguments; diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 40adbaed6..3e8839f17 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -562,11 +562,11 @@ internal static string SettingRuleArgumentKeyIsNotUniqueIgnoringCase { } /// - /// Looks up a localized string similar to Setting rule '{0}' argument value for key '{1}' should be nonnull. + /// Looks up a localized string similar to The setting 'Rules', rule '{0}', argument '{1}' value is null. /// - internal static string SettingRuleArgumentValueShouldBeNonNull { + internal static string SettingRuleArgumentValueIsNull { get { - return ResourceManager.GetString("SettingRuleArgumentValueShouldBeNonNull", resourceCulture); + return ResourceManager.GetString("SettingRuleArgumentValueIsNull", resourceCulture); } } diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 28f52a15d..b534e98c4 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -286,10 +286,10 @@ The setting 'Rules', rule '{0}', argument key '{1}' is not unique, ignoring case. - - - Setting rule '{0}' argument value for key '{1}' should be nonnull. + + The setting 'Rules', rule '{0}', argument '{1}' value is null. + Dictionary should be indexable in a case-insensitive manner. From 82bc7d04ba1522f3436a3925c9492e720a3cbe57 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 08:56:03 -0500 Subject: [PATCH 47/75] Add temp TODOs --- Engine/Settings.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index dcda1f891..06fdcc42e 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -252,6 +252,7 @@ private bool IsStringOrStringArray(object val) return val == null ? false : valArr.All(x => x is string); } + // TODO Clean up method GetData(object, string). private List GetData(object val, string key) { // value must be either string or or an array of strings @@ -349,23 +350,30 @@ private void parseSettingsHashtable(Hashtable settings) switch (settingName) { + // TODO Clean up "Severity" setting validating parsing. case "severity": severities = GetData(setting.Value, settingName); break; + // TODO Clean up "IncludeRules" setting validating parsing. case "includerules": includeRules = GetData(setting.Value, settingName); break; + // TODO Clean up "ExcludeRules" setting validating parsing. case "excluderules": excludeRules = GetData(setting.Value, settingName); break; + // TODO Clean up "CustomRulePath" setting validating parsing. case "customrulepath": customRulePath = GetData(setting.Value, settingName); break; + // TODO Clean up "IncludeDefaultRules" setting validating parsing. case "includedefaultrules": + + // TODO Clean up "RecurseCustomRulePath" setting validating parsing. case "recursecustomrulepath": if (!(setting.Value is bool)) { @@ -478,6 +486,7 @@ private void parseSettingsHashtable(Hashtable settings) this.ruleArguments = parsedRules; break; + // TODO Clean up default setting validating parsing. default: throw new InvalidDataException( string.Format( From 39d0063b65512ff041f6a77fd133dd36ea95e1e8 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 09:12:31 -0500 Subject: [PATCH 48/75] Rename GetData interface --- Engine/Settings.cs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 06fdcc42e..ff7a6c74a 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -253,32 +253,32 @@ private bool IsStringOrStringArray(object val) } // TODO Clean up method GetData(object, string). - private List GetData(object val, string key) + private List ParseSettingValueAsStrings(object value, string settingName) { // value must be either string or or an array of strings - if (val == null) + if (value == null) { throw new InvalidDataException( string.Format( CultureInfo.CurrentCulture, Strings.WrongValueHashTable, "", - key)); + settingName)); } List values = new List(); - var valueStr = val as string; + var valueStr = value as string; if (valueStr != null) { values.Add(valueStr); } else { - var valueArr = val as object[]; + var valueArr = value as object[]; if (valueArr == null) { // check if it is an array of strings - valueArr = val as string[]; + valueArr = value as string[]; } if (valueArr != null) @@ -296,8 +296,8 @@ private List GetData(object val, string key) string.Format( CultureInfo.CurrentCulture, Strings.WrongValueHashTable, - val, - key)); + value, + settingName)); } } } @@ -307,8 +307,8 @@ private List GetData(object val, string key) string.Format( CultureInfo.CurrentCulture, Strings.WrongValueHashTable, - val, - key)); + value, + settingName)); } } @@ -352,22 +352,22 @@ private void parseSettingsHashtable(Hashtable settings) { // TODO Clean up "Severity" setting validating parsing. case "severity": - severities = GetData(setting.Value, settingName); + this.severities = ParseSettingValueAsStrings(setting.Value, settingName); break; // TODO Clean up "IncludeRules" setting validating parsing. case "includerules": - includeRules = GetData(setting.Value, settingName); + this.includeRules = ParseSettingValueAsStrings(setting.Value, settingName); break; // TODO Clean up "ExcludeRules" setting validating parsing. case "excluderules": - excludeRules = GetData(setting.Value, settingName); + this.excludeRules = ParseSettingValueAsStrings(setting.Value, settingName); break; // TODO Clean up "CustomRulePath" setting validating parsing. case "customrulepath": - customRulePath = GetData(setting.Value, settingName); + this.customRulePath = ParseSettingValueAsStrings(setting.Value, settingName); break; // TODO Clean up "IncludeDefaultRules" setting validating parsing. From ceda89f27868c5c826169795a703b11235fb926e Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 09:22:28 -0500 Subject: [PATCH 49/75] Add TODOs in GetData (now ParseSettingValueAsStrings) --- Engine/Settings.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index ff7a6c74a..7cdb80500 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -255,6 +255,14 @@ private bool IsStringOrStringArray(object val) // TODO Clean up method GetData(object, string). private List ParseSettingValueAsStrings(object value, string settingName) { + // TODO Validate that value is not null. + + // TODO Check if value is a string, and if so then convert it to a string array. + + // TODO Validate that value is an IList, and type it if so. + + // TODO Validate that each element of the IList value is a string, and type it if so. + // value must be either string or or an array of strings if (value == null) { From 9f6e3241a5896de34fd5d04d1b0149ceeb5ab909 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 09:27:05 -0500 Subject: [PATCH 50/75] Validate that the string(s) setting value is nonnull --- Engine/Settings.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 7cdb80500..3f24b10d2 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -255,7 +255,12 @@ private bool IsStringOrStringArray(object val) // TODO Clean up method GetData(object, string). private List ParseSettingValueAsStrings(object value, string settingName) { - // TODO Validate that value is not null. + if (value == null) + { + throw new InvalidDataException(string.Format( + Strings.SettingValueIsNull, + settingName)); + } // TODO Check if value is a string, and if so then convert it to a string array. @@ -263,16 +268,7 @@ private List ParseSettingValueAsStrings(object value, string settingName // TODO Validate that each element of the IList value is a string, and type it if so. - // value must be either string or or an array of strings - if (value == null) - { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.WrongValueHashTable, - "", - settingName)); - } + List values = new List(); var valueStr = value as string; From 6ced092668028e15d36d4939ce268162127d5a48 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 09:30:50 -0500 Subject: [PATCH 51/75] Convert a single string value to an unary array --- Engine/Settings.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 3f24b10d2..e8dd36c96 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -262,7 +262,10 @@ private List ParseSettingValueAsStrings(object value, string settingName settingName)); } - // TODO Check if value is a string, and if so then convert it to a string array. + if (value is string) + { + value = new[] { value }; + } // TODO Validate that value is an IList, and type it if so. From 7b85fbe1721998852ff3f7e69b4ca03397cfaa41 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 09:41:54 -0500 Subject: [PATCH 52/75] Validate that the setting value is a collection --- Engine/Settings.cs | 8 +++++++- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index e8dd36c96..dc71bfe12 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -267,7 +267,13 @@ private List ParseSettingValueAsStrings(object value, string settingName value = new[] { value }; } - // TODO Validate that value is an IList, and type it if so. + if (!(value is ICollection)) + { + throw new InvalidDataException(string.Format( + Strings.SettingValueIsNotStringOrStringsType, + settingName)); + } + ICollection values = value as ICollection; // TODO Validate that each element of the IList value is a string, and type it if so. diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 3e8839f17..431fcf9b7 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -480,6 +480,15 @@ internal static string SettingValueIsNull { } } + /// + /// Looks up a localized string similar to The setting '{0}' value '{1}' is not a string or collection of strings type. + /// + internal static string SettingValueIsNotStringOrStringsType { + get { + return ResourceManager.GetString("SettingValueIsNotStringOrStringsType", resourceCulture); + } + } + /// /// Looks up a localized string similar to The setting 'Rules' value '{0}' is not a dictionary type. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index b534e98c4..d9bc0edd1 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -259,6 +259,9 @@ The setting '{0}' value is null. + + The setting '{0}' value '{1}' is not a string or collection of strings type. + The setting 'Rules' value '{0}' is not a dictionary type. From 1433176eb97d022468bfaf24665d98219fd88612 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 09:57:44 -0500 Subject: [PATCH 53/75] Validate that each strings setting value element is nonnull --- Engine/Settings.cs | 18 +++++++++++++++++- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index dc71bfe12..321cab491 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -276,8 +276,24 @@ private List ParseSettingValueAsStrings(object value, string settingName ICollection values = value as ICollection; // TODO Validate that each element of the IList value is a string, and type it if so. + IList strings = new List(values.Count); + int elementIndex = 0; + foreach (var element in values) + { + if (element is null) + { + throw new InvalidDataException(string.Format( + Strings.SettingValueElementIsNull, + settingName, + elementIndex)); + } + + // TODO Validate that each element is a string, and if so then add it to the strings list. + + elementIndex += 1; + } - + List values = new List(); var valueStr = value as string; diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 431fcf9b7..2fcc8134a 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -489,6 +489,15 @@ internal static string SettingValueIsNotStringOrStringsType { } } + /// + /// Looks up a localized string similar to The setting '{0}' value, index {1} element is null. + /// + internal static string SettingValueElementIsNull { + get { + return ResourceManager.GetString("SettingValueElementIsNull", resourceCulture); + } + } + /// /// Looks up a localized string similar to The setting 'Rules' value '{0}' is not a dictionary type. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index d9bc0edd1..d732f19e5 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -262,6 +262,9 @@ The setting '{0}' value '{1}' is not a string or collection of strings type. + + The setting '{0}', index {1} element is null. + The setting 'Rules' value '{0}' is not a dictionary type. From 17a3a42b4d175e93634be9907e7937a841606545 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 10:02:59 -0500 Subject: [PATCH 54/75] Validate that each strings setting value element is a string type --- Engine/Settings.cs | 11 +++++++++-- Engine/Strings.Designer.cs | 9 +++++++++ Engine/Strings.resx | 3 +++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 321cab491..36cbd7e18 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -275,7 +275,6 @@ private List ParseSettingValueAsStrings(object value, string settingName } ICollection values = value as ICollection; - // TODO Validate that each element of the IList value is a string, and type it if so. IList strings = new List(values.Count); int elementIndex = 0; foreach (var element in values) @@ -288,7 +287,15 @@ private List ParseSettingValueAsStrings(object value, string settingName elementIndex)); } - // TODO Validate that each element is a string, and if so then add it to the strings list. + if (!(element is string)) + { + throw new InvalidDataException(string.Format( + Strings.SettingValueElementIsNotStringType, + settingName, + elementIndex, + element)); + } + strings.Add(element as string); elementIndex += 1; } diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 2fcc8134a..9b91b4a27 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -498,6 +498,15 @@ internal static string SettingValueElementIsNull { } } + /// + /// Looks up a localized string similar to The setting '{0}', index {1} element, value '{2}' is not a string type. + /// + internal static string SettingValueElementIsNotStringType { + get { + return ResourceManager.GetString("SettingValueElementIsNotStringType", resourceCulture); + } + } + /// /// Looks up a localized string similar to The setting 'Rules' value '{0}' is not a dictionary type. /// diff --git a/Engine/Strings.resx b/Engine/Strings.resx index d732f19e5..2a97cee56 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -265,6 +265,9 @@ The setting '{0}', index {1} element is null. + + The setting '{0}', index {1} element, value '{2}' is not a string type. + The setting 'Rules' value '{0}' is not a dictionary type. From f3a35d26d63924e0e9a1278c69c4161b612d5768 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 10:06:31 -0500 Subject: [PATCH 55/75] Tie off refactor GetData, and name it ParseSettingValueStringOrStrings --- Engine/Settings.cs | 65 ++++++---------------------------------------- 1 file changed, 8 insertions(+), 57 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 36cbd7e18..a89ec070f 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -252,8 +252,7 @@ private bool IsStringOrStringArray(object val) return val == null ? false : valArr.All(x => x is string); } - // TODO Clean up method GetData(object, string). - private List ParseSettingValueAsStrings(object value, string settingName) + private List ParseSettingValueStringOrStrings(object value, string settingName) { if (value == null) { @@ -273,9 +272,9 @@ private List ParseSettingValueAsStrings(object value, string settingName Strings.SettingValueIsNotStringOrStringsType, settingName)); } - ICollection values = value as ICollection; + var values = value as ICollection; - IList strings = new List(values.Count); + var strings = new List(values.Count); int elementIndex = 0; foreach (var element in values) { @@ -299,56 +298,8 @@ private List ParseSettingValueAsStrings(object value, string settingName elementIndex += 1; } - - - - List values = new List(); - var valueStr = value as string; - if (valueStr != null) - { - values.Add(valueStr); - } - else - { - var valueArr = value as object[]; - if (valueArr == null) - { - // check if it is an array of strings - valueArr = value as string[]; - } - - if (valueArr != null) - { - foreach (var item in valueArr) - { - var itemStr = item as string; - if (itemStr != null) - { - values.Add(itemStr); - } - else - { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.WrongValueHashTable, - value, - settingName)); - } - } - } - else - { - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.WrongValueHashTable, - value, - settingName)); - } - } - return values; + return strings; } private void parseSettingsHashtable(Hashtable settings) @@ -388,22 +339,22 @@ private void parseSettingsHashtable(Hashtable settings) { // TODO Clean up "Severity" setting validating parsing. case "severity": - this.severities = ParseSettingValueAsStrings(setting.Value, settingName); + this.severities = ParseSettingValueStringOrStrings(setting.Value, settingName); break; // TODO Clean up "IncludeRules" setting validating parsing. case "includerules": - this.includeRules = ParseSettingValueAsStrings(setting.Value, settingName); + this.includeRules = ParseSettingValueStringOrStrings(setting.Value, settingName); break; // TODO Clean up "ExcludeRules" setting validating parsing. case "excluderules": - this.excludeRules = ParseSettingValueAsStrings(setting.Value, settingName); + this.excludeRules = ParseSettingValueStringOrStrings(setting.Value, settingName); break; // TODO Clean up "CustomRulePath" setting validating parsing. case "customrulepath": - this.customRulePath = ParseSettingValueAsStrings(setting.Value, settingName); + this.customRulePath = ParseSettingValueStringOrStrings(setting.Value, settingName); break; // TODO Clean up "IncludeDefaultRules" setting validating parsing. From 6d974f0617b4a7e011d459ed63a80c7f2f1809d0 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:09:23 -0500 Subject: [PATCH 56/75] Debug overzealous application of string.ToLowerInvariant() Logically reverts commit 07cb4e33386d66dd03e155f87350eec3e102b43f. --- Engine/Settings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index a89ec070f..f7f723ae2 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -401,7 +401,7 @@ private void parseSettingsHashtable(Hashtable settings) Strings.SettingRuleKeyIsNotStringType, rule.Key)); } - string ruleName = (rule.Key as string).ToLowerInvariant(); + string ruleName = rule.Key as string; if (!uniqueRuleKeys.Add(ruleName)) { @@ -445,7 +445,7 @@ private void parseSettingsHashtable(Hashtable settings) ruleName, argument.Key)); } - string argumentName = (argument.Key as string).ToLowerInvariant(); + string argumentName = argument.Key as string; if (!uniqueArgumentKeys.Add(argumentName)) { From 1c1022c39abdedb96a377e237e4a41675e67c5b8 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:11:30 -0500 Subject: [PATCH 57/75] Standardize legacy method capitalization --- Engine/Settings.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index f7f723ae2..d0c1b1ec9 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -73,7 +73,7 @@ public Settings(object settings, Func presetResolver) if (File.Exists(settingsFilePath)) { filePath = settingsFilePath; - parseSettingsFile(settingsFilePath); + ParseSettingsFile(settingsFilePath); } else { @@ -89,7 +89,7 @@ public Settings(object settings, Func presetResolver) var settingsHashtable = settings as Hashtable; if (settingsHashtable != null) { - parseSettingsHashtable(settingsHashtable); + ParseSettingsHashtable(settingsHashtable); } else { @@ -302,7 +302,7 @@ private List ParseSettingValueStringOrStrings(object value, string setti return strings; } - private void parseSettingsHashtable(Hashtable settings) + private void ParseSettingsHashtable(Hashtable settings) { ISet uniqueSettingKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry setting in settings) @@ -484,7 +484,7 @@ private void parseSettingsHashtable(Hashtable settings) } } - private void parseSettingsFile(string settingsFilePath) + private void ParseSettingsFile(string settingsFilePath) { Token[] parserTokens = null; ParseError[] parserErrors = null; @@ -519,7 +519,7 @@ private void parseSettingsFile(string settingsFilePath) settingsFilePath)); } - parseSettingsHashtable(hashtable); + ParseSettingsHashtable(hashtable); } /// From fdc85cfbc70f050652439a750d473adfac6c6bde Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:14:23 -0500 Subject: [PATCH 58/75] Update completed TODO comments --- Engine/Settings.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index d0c1b1ec9..e0d0f2ab4 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -337,22 +337,18 @@ private void ParseSettingsHashtable(Hashtable settings) switch (settingName) { - // TODO Clean up "Severity" setting validating parsing. case "severity": this.severities = ParseSettingValueStringOrStrings(setting.Value, settingName); break; - // TODO Clean up "IncludeRules" setting validating parsing. case "includerules": this.includeRules = ParseSettingValueStringOrStrings(setting.Value, settingName); break; - // TODO Clean up "ExcludeRules" setting validating parsing. case "excluderules": this.excludeRules = ParseSettingValueStringOrStrings(setting.Value, settingName); break; - // TODO Clean up "CustomRulePath" setting validating parsing. case "customrulepath": this.customRulePath = ParseSettingValueStringOrStrings(setting.Value, settingName); break; From 1177b9a41aae04dd8958936c1d0e5ef3e381766d Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:24:32 -0500 Subject: [PATCH 59/75] Debug capitalization of setting names in exception messages --- Engine/Settings.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index e0d0f2ab4..66e26f1b2 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -318,7 +318,7 @@ private void ParseSettingsHashtable(Hashtable settings) Strings.SettingKeyIsNotStringType, setting.Key)); } - string settingName = (setting.Key as string).ToLowerInvariant(); // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 + string settingName = setting.Key as string; if (!uniqueSettingKeys.Add(settingName)) { @@ -335,7 +335,8 @@ private void ParseSettingsHashtable(Hashtable settings) settingName)); } - switch (settingName) + // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 + switch (settingName.ToLowerInvariant()) { case "severity": this.severities = ParseSettingValueStringOrStrings(setting.Value, settingName); From c5889170391183d191f1544c78309aab10d462fd Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:30:28 -0500 Subject: [PATCH 60/75] Scaffold method ParseSettingValueBoolean(object, string) --- Engine/Settings.cs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 66e26f1b2..0fdfa59f5 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -302,6 +302,21 @@ private List ParseSettingValueStringOrStrings(object value, string setti return strings; } + private bool ParseSettingValueBoolean(object value, string settingName) + { + // TODO Clean up body of ParseSettingValueBoolean(object, string). + if (!(value is bool)) + { + throw new InvalidDataException(string.Format( + CultureInfo.CurrentCulture, + Strings.SettingsValueTypeMustBeBool, + settingName)); + } + + var booleanVal = (bool)value; + return booleanVal; + } + private void ParseSettingsHashtable(Hashtable settings) { ISet uniqueSettingKeys = new HashSet(StringComparer.OrdinalIgnoreCase); @@ -359,19 +374,7 @@ private void ParseSettingsHashtable(Hashtable settings) // TODO Clean up "RecurseCustomRulePath" setting validating parsing. case "recursecustomrulepath": - if (!(setting.Value is bool)) - { - throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.SettingsValueTypeMustBeBool, - setting.Key)); - } - - var booleanVal = (bool)setting.Value; - var field = this.GetType().GetField( - settingName, - BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.NonPublic); - field.SetValue(this, booleanVal); + break; case "rules": From 275c7a802517fe46b6cc54ddd48799e2202ec58b Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:31:55 -0500 Subject: [PATCH 61/75] Parse settings 'IncludeDefaultRules' and 'RecurseCustomRulePath' with ParseSettingValueBoolean(object, string) --- Engine/Settings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 0fdfa59f5..189ebdbf5 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -369,12 +369,12 @@ private void ParseSettingsHashtable(Hashtable settings) this.customRulePath = ParseSettingValueStringOrStrings(setting.Value, settingName); break; - // TODO Clean up "IncludeDefaultRules" setting validating parsing. case "includedefaultrules": + this.includeDefaultRules = ParseSettingValueBoolean(setting.Value, settingName); + break; - // TODO Clean up "RecurseCustomRulePath" setting validating parsing. case "recursecustomrulepath": - + this.includeDefaultRules = ParseSettingValueBoolean(setting.Value, settingName); break; case "rules": From 4e687354769845d5660d4a3894e730596c78a08a Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:34:22 -0500 Subject: [PATCH 62/75] Validate that boolean settings value is nonnull --- Engine/Settings.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 189ebdbf5..e66bd73fb 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -304,6 +304,13 @@ private List ParseSettingValueStringOrStrings(object value, string setti private bool ParseSettingValueBoolean(object value, string settingName) { + if (value == null) + { + throw new InvalidDataException(string.Format( + Strings.SettingValueIsNull, + settingName)); + } + // TODO Clean up body of ParseSettingValueBoolean(object, string). if (!(value is bool)) { From 754c008fbe319edd2990bbb9f54e67d11adae630 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:37:13 -0500 Subject: [PATCH 63/75] Validate that boolean setting value is a boolean type --- Engine/Settings.cs | 8 ++++---- Engine/Strings.Designer.cs | 11 ++++++++++- Engine/Strings.resx | 3 +++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index e66bd73fb..7bcb2ccb0 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -311,15 +311,15 @@ private bool ParseSettingValueBoolean(object value, string settingName) settingName)); } - // TODO Clean up body of ParseSettingValueBoolean(object, string). if (!(value is bool)) { throw new InvalidDataException(string.Format( - CultureInfo.CurrentCulture, - Strings.SettingsValueTypeMustBeBool, - settingName)); + Strings.SettingValueIsNotBooleanType, + settingName, + value)); } + // TODO Clean up body of ParseSettingValueBoolean(object, string). var booleanVal = (bool)value; return booleanVal; } diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 9b91b4a27..705b254b5 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -480,6 +480,15 @@ internal static string SettingValueIsNull { } } + /// + /// Looks up a localized string similar to The setting '{0}' value '{1}' is not a boolean type. + /// + internal static string SettingValueIsNotBooleanType { + get { + return ResourceManager.GetString("SettingValueIsNotBooleanType", resourceCulture); + } + } + /// /// Looks up a localized string similar to The setting '{0}' value '{1}' is not a string or collection of strings type. /// @@ -490,7 +499,7 @@ internal static string SettingValueIsNotStringOrStringsType { } /// - /// Looks up a localized string similar to The setting '{0}' value, index {1} element is null. + /// Looks up a localized string similar to The setting '{0}', index {1} element is null. /// internal static string SettingValueElementIsNull { get { diff --git a/Engine/Strings.resx b/Engine/Strings.resx index 2a97cee56..9eccd14e5 100644 --- a/Engine/Strings.resx +++ b/Engine/Strings.resx @@ -259,6 +259,9 @@ The setting '{0}' value is null. + + The setting '{0}' value '{1}' is not a boolean type. + The setting '{0}' value '{1}' is not a string or collection of strings type. From be955e3130c2bef81ba267457156b28b3b87a5eb Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:39:37 -0500 Subject: [PATCH 64/75] Complete parsing of boolean setting value --- Engine/Settings.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 7bcb2ccb0..35abe2ee6 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -319,9 +319,7 @@ private bool ParseSettingValueBoolean(object value, string settingName) value)); } - // TODO Clean up body of ParseSettingValueBoolean(object, string). - var booleanVal = (bool)value; - return booleanVal; + return (bool) value; } private void ParseSettingsHashtable(Hashtable settings) From 26a7ddea0fa915c93e3285999b7fe8d42f1a32fd Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 12:31:12 -0500 Subject: [PATCH 65/75] Debug field assignment when parsing setting 'RecurseCustomRulePath' --- Engine/Settings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 35abe2ee6..d14bc6be2 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -379,7 +379,7 @@ private void ParseSettingsHashtable(Hashtable settings) break; case "recursecustomrulepath": - this.includeDefaultRules = ParseSettingValueBoolean(setting.Value, settingName); + this.recurseCustomRulePath = ParseSettingValueBoolean(setting.Value, settingName); break; case "rules": From dca15a8271d3110d130d4f09c598354dcf40dde8 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 19:22:27 -0500 Subject: [PATCH 66/75] Clean up unrecognized setting key handling --- Engine/Settings.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index d14bc6be2..5e316395f 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -478,13 +478,10 @@ private void ParseSettingsHashtable(Hashtable settings) this.ruleArguments = parsedRules; break; - // TODO Clean up default setting validating parsing. default: - throw new InvalidDataException( - string.Format( - CultureInfo.CurrentCulture, - Strings.WrongKeyHashTable, - settingName)); + throw new InvalidDataException(string.Format( + Strings.WrongKeyHashTable, + settingName)); } } } From ea0d1b60b50235645f760487c4c8be59e03746a2 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 19:48:23 -0500 Subject: [PATCH 67/75] Aggregate exceptions within ParseSetingsHashtable(Hashtable) --- Engine/Settings.cs | 86 +++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 5e316395f..4f5acb0a8 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -324,70 +324,84 @@ private bool ParseSettingValueBoolean(object value, string settingName) private void ParseSettingsHashtable(Hashtable settings) { + IList exceptions = new List(); + ISet uniqueSettingKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry setting in settings) { if (setting.Key is null) { - throw new InvalidDataException(Strings.SettingKeyIsNull); + exceptions.Add(new InvalidDataException( + Strings.SettingKeyIsNull)); + continue; } if (!(setting.Key is string)) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingKeyIsNotStringType, - setting.Key)); + setting.Key))); + continue; } string settingName = setting.Key as string; if (!uniqueSettingKeys.Add(settingName)) { // setting.Key should be used instead of settingName because the former preserves information about the source casing. - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingKeyIsNotUniqueIgnoringCase, - setting.Key)); + setting.Key))); + continue; } if (setting.Value is null) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingValueIsNull, - settingName)); + settingName))); + continue; } // ToLowerInvariant is important to also work with turkish culture, see https://github.com/PowerShell/PSScriptAnalyzer/issues/1095 switch (settingName.ToLowerInvariant()) { case "severity": + // TODO Aggregate exceptions. this.severities = ParseSettingValueStringOrStrings(setting.Value, settingName); break; case "includerules": + // TODO Aggregate exceptions. this.includeRules = ParseSettingValueStringOrStrings(setting.Value, settingName); break; case "excluderules": + // TODO Aggregate exceptions. this.excludeRules = ParseSettingValueStringOrStrings(setting.Value, settingName); break; case "customrulepath": + // TODO Aggregate exceptions. this.customRulePath = ParseSettingValueStringOrStrings(setting.Value, settingName); break; case "includedefaultrules": + // TODO Aggregate exceptions. this.includeDefaultRules = ParseSettingValueBoolean(setting.Value, settingName); break; case "recursecustomrulepath": + // TODO Aggregate exceptions. this.recurseCustomRulePath = ParseSettingValueBoolean(setting.Value, settingName); break; case "rules": if (!(setting.Value is System.Collections.IDictionary)) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRulesValueIsNotDictionaryType, - setting.Value)); + setting.Value))); + continue; } Hashtable rules = setting.Value as Hashtable; @@ -397,38 +411,44 @@ private void ParseSettingsHashtable(Hashtable settings) { if (rule.Key is null) { - throw new InvalidDataException(Strings.SettingRuleKeyIsNull); + exceptions.Add(new InvalidDataException( + Strings.SettingRuleKeyIsNull)); + continue; } if (!(rule.Key is string)) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRuleKeyIsNotStringType, - rule.Key)); + rule.Key))); + continue; } string ruleName = rule.Key as string; if (!uniqueRuleKeys.Add(ruleName)) { // rule.Key should be used instead of ruleName because the former preserves information about the source casing. - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRuleKeyIsNotUniqueIgnoringCase, - rule.Key)); + rule.Key))); + continue; } if (rule.Value is null) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRuleValueIsNull, - ruleName)); + ruleName))); + continue; } if (!(rule.Value is System.Collections.IDictionary)) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRuleValueIsNotDictionaryType, ruleName, - rule.Value)); + rule.Value))); + continue; } Hashtable arguments = rule.Value as Hashtable; @@ -438,35 +458,39 @@ private void ParseSettingsHashtable(Hashtable settings) { if (argument.Key is null) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRuleArgumentKeyIsNull, - ruleName)); + ruleName))); + continue; } if (!(argument.Key is string)) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRuleArgumentKeyIsNotStringType, ruleName, - argument.Key)); + argument.Key))); + continue; } string argumentName = argument.Key as string; if (!uniqueArgumentKeys.Add(argumentName)) { // argument.Key should be used instead of argumentName because the former preserves information about the source casing. - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRuleArgumentKeyIsNotUniqueIgnoringCase, ruleName, - argument.Key)); + argument.Key))); + continue; } if (argument.Value is null) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRuleArgumentValueIsNull, ruleName, - argumentName)); + argumentName))); + continue; } parsedArguments[argumentName] = argument.Value; @@ -479,11 +503,17 @@ private void ParseSettingsHashtable(Hashtable settings) break; default: - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.WrongKeyHashTable, - settingName)); + settingName))); + continue; } } + + if (exceptions.Count > 0) + { + throw new AggregateException(exceptions); + } } private void ParseSettingsFile(string settingsFilePath) From f43dc672b9d1463fbaf6407b7d535e3c38b384ff Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 20:12:14 -0500 Subject: [PATCH 68/75] Aggregate exceptions in ParseSettingValueBoolean(object, string) by adding IList parameter --- Engine/Settings.cs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 4f5acb0a8..2e223c791 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -302,21 +302,23 @@ private List ParseSettingValueStringOrStrings(object value, string setti return strings; } - private bool ParseSettingValueBoolean(object value, string settingName) + private bool? ParseSettingValueBoolean(object value, string settingName, IList exceptions) { if (value == null) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingValueIsNull, - settingName)); + settingName))); + return null; } if (!(value is bool)) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingValueIsNotBooleanType, settingName, - value)); + value))); + return null; } return (bool) value; @@ -386,13 +388,23 @@ private void ParseSettingsHashtable(Hashtable settings) break; case "includedefaultrules": - // TODO Aggregate exceptions. - this.includeDefaultRules = ParseSettingValueBoolean(setting.Value, settingName); + bool? maybeIncludeDefaultRules = ParseSettingValueBoolean(setting.Value, settingName, exceptions); + if (maybeIncludeDefaultRules is null) + { + continue; + } + + this.includeDefaultRules = (bool) maybeIncludeDefaultRules; break; case "recursecustomrulepath": - // TODO Aggregate exceptions. - this.recurseCustomRulePath = ParseSettingValueBoolean(setting.Value, settingName); + bool? maybeRecurseCustomRulePath = ParseSettingValueBoolean(setting.Value, settingName, exceptions); + if (maybeRecurseCustomRulePath is null) + { + continue; + } + + this.recurseCustomRulePath = (bool) maybeRecurseCustomRulePath; break; case "rules": From 09222c618edab58c2f6b811dabdb01fd3e94da6e Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Mon, 24 Jun 2019 20:25:07 -0500 Subject: [PATCH 69/75] Aggregate exceptions in ParseSettingValueStringOrStrings(object, string) by adding IList parameter --- Engine/Settings.cs | 58 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 2e223c791..cab53f068 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -252,13 +252,14 @@ private bool IsStringOrStringArray(object val) return val == null ? false : valArr.All(x => x is string); } - private List ParseSettingValueStringOrStrings(object value, string settingName) + private List ParseSettingValueStringOrStrings(object value, string settingName, IList exceptions) { if (value == null) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingValueIsNull, - settingName)); + settingName))); + return null; } if (value is string) @@ -268,9 +269,10 @@ private List ParseSettingValueStringOrStrings(object value, string setti if (!(value is ICollection)) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingValueIsNotStringOrStringsType, - settingName)); + settingName))); + return null; } var values = value as ICollection; @@ -280,19 +282,21 @@ private List ParseSettingValueStringOrStrings(object value, string setti { if (element is null) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingValueElementIsNull, settingName, - elementIndex)); + elementIndex))); + continue; } if (!(element is string)) { - throw new InvalidDataException(string.Format( + exceptions.Add(new InvalidDataException(string.Format( Strings.SettingValueElementIsNotStringType, settingName, elementIndex, - element)); + element))); + continue; } strings.Add(element as string); @@ -368,23 +372,43 @@ private void ParseSettingsHashtable(Hashtable settings) switch (settingName.ToLowerInvariant()) { case "severity": - // TODO Aggregate exceptions. - this.severities = ParseSettingValueStringOrStrings(setting.Value, settingName); + var maybeSeverity = ParseSettingValueStringOrStrings(setting.Value, settingName, exceptions); + if (maybeSeverity is null) + { + continue; + } + + this.severities = maybeSeverity; break; case "includerules": - // TODO Aggregate exceptions. - this.includeRules = ParseSettingValueStringOrStrings(setting.Value, settingName); + var maybeIncludeRules = ParseSettingValueStringOrStrings(setting.Value, settingName, exceptions); + if (maybeIncludeRules is null) + { + continue; + } + + this.includeRules = maybeIncludeRules; break; case "excluderules": - // TODO Aggregate exceptions. - this.excludeRules = ParseSettingValueStringOrStrings(setting.Value, settingName); + var maybeExcludeRules = ParseSettingValueStringOrStrings(setting.Value, settingName, exceptions); + if (maybeExcludeRules is null) + { + continue; + } + + this.excludeRules = maybeExcludeRules; break; case "customrulepath": - // TODO Aggregate exceptions. - this.customRulePath = ParseSettingValueStringOrStrings(setting.Value, settingName); + var maybeCustomRulePath = ParseSettingValueStringOrStrings(setting.Value, settingName, exceptions); + if (maybeCustomRulePath is null) + { + continue; + } + + this.customRulePath = maybeCustomRulePath; break; case "includedefaultrules": From cae559c072609c0f95035b335cfaf10783192105 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Sun, 30 Jun 2019 04:23:07 -0500 Subject: [PATCH 70/75] Debug exceptional element index incrementing in method Settings.ParseSettingValueStringOrStrings(object, string, IList) --- Engine/Settings.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index cab53f068..363a94d13 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -278,14 +278,17 @@ private List ParseSettingValueStringOrStrings(object value, string setti var strings = new List(values.Count); int elementIndex = 0; + int currentElementIndex = elementIndex; foreach (var element in values) { + currentElementIndex = elementIndex++; + if (element is null) { exceptions.Add(new InvalidDataException(string.Format( Strings.SettingValueElementIsNull, settingName, - elementIndex))); + currentElementIndex))); continue; } @@ -294,13 +297,12 @@ private List ParseSettingValueStringOrStrings(object value, string setti exceptions.Add(new InvalidDataException(string.Format( Strings.SettingValueElementIsNotStringType, settingName, - elementIndex, + currentElementIndex, element))); continue; } + strings.Add(element as string); - - elementIndex += 1; } return strings; From cf2504b69b4cf6f9bb98c3f21a73bd4ff11767ce Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Thu, 4 Jul 2019 18:55:26 -0500 Subject: [PATCH 71/75] Trim trailing whitespace --- Engine/Settings.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 363a94d13..fc13fa81d 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -266,7 +266,7 @@ private List ParseSettingValueStringOrStrings(object value, string setti { value = new[] { value }; } - + if (!(value is ICollection)) { exceptions.Add(new InvalidDataException(string.Format( @@ -282,7 +282,7 @@ private List ParseSettingValueStringOrStrings(object value, string setti foreach (var element in values) { currentElementIndex = elementIndex++; - + if (element is null) { exceptions.Add(new InvalidDataException(string.Format( @@ -317,7 +317,7 @@ private List ParseSettingValueStringOrStrings(object value, string setti settingName))); return null; } - + if (!(value is bool)) { exceptions.Add(new InvalidDataException(string.Format( @@ -333,7 +333,7 @@ private List ParseSettingValueStringOrStrings(object value, string setti private void ParseSettingsHashtable(Hashtable settings) { IList exceptions = new List(); - + ISet uniqueSettingKeys = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry setting in settings) { @@ -343,7 +343,7 @@ private void ParseSettingsHashtable(Hashtable settings) Strings.SettingKeyIsNull)); continue; } - + if (!(setting.Key is string)) { exceptions.Add(new InvalidDataException(string.Format( @@ -429,7 +429,7 @@ private void ParseSettingsHashtable(Hashtable settings) { continue; } - + this.recurseCustomRulePath = (bool) maybeRecurseCustomRulePath; break; @@ -511,7 +511,7 @@ private void ParseSettingsHashtable(Hashtable settings) continue; } string argumentName = argument.Key as string; - + if (!uniqueArgumentKeys.Add(argumentName)) { // argument.Key should be used instead of argumentName because the former preserves information about the source casing. @@ -521,7 +521,7 @@ private void ParseSettingsHashtable(Hashtable settings) argument.Key))); continue; } - + if (argument.Value is null) { exceptions.Add(new InvalidDataException(string.Format( From c7ff89b09eceeb23a02e2d18caf526debd546d14 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Thu, 4 Jul 2019 19:04:43 -0500 Subject: [PATCH 72/75] Do not `continue` if a "Rules" setting key is non-unique --- Engine/Settings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index fc13fa81d..2f50b71fd 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -469,7 +469,7 @@ private void ParseSettingsHashtable(Hashtable settings) exceptions.Add(new InvalidDataException(string.Format( Strings.SettingRuleKeyIsNotUniqueIgnoringCase, rule.Key))); - continue; + // Do not `continue` because even if an element's key is non-unique, that element's value may still be checked. } if (rule.Value is null) From d54384fdf0073d7970e9eff0dd52806136fb3952 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Thu, 4 Jul 2019 20:17:29 -0500 Subject: [PATCH 73/75] Refactor `Settings` constructor to use the guard chain pattern --- Engine/Settings.cs | 53 ++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 2f50b71fd..89a459ec4 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -52,13 +52,13 @@ public Settings(object settings, Func presetResolver) throw new ArgumentNullException(nameof(settings)); } - includeRules = new List(); - excludeRules = new List(); - severities = new List(); - ruleArguments = new Dictionary>(StringComparer.OrdinalIgnoreCase); - var settingsFilePath = settings as string; + this.includeRules = new List(); + this.excludeRules = new List(); + this.severities = new List(); + this.ruleArguments = new Dictionary>(StringComparer.OrdinalIgnoreCase); - //it can either be a preset or path to a file or a hashtable + // If `settings` is a string, then preprocess it by (1) resolving it to a file path, and (2) parsing the file to a Hashtable. + var settingsFilePath = settings as string; if (settingsFilePath != null) { if (presetResolver != null) @@ -68,34 +68,37 @@ public Settings(object settings, Func presetResolver) { settingsFilePath = resolvedFilePath; } + // Do not throw an exception if `presetResolver` fails to resolve `settingsFilePath`. Rather, attempt to handle the issue + // ourselves by acting simply as if no `presetResolver` was passed in the first place. } + // Do not throw an exception if the `presetResolver` argument is null. This is because it is permitted for a file path `settings` to + // not have any associated `presetResolver`. if (File.Exists(settingsFilePath)) { - filePath = settingsFilePath; + this.filePath = settingsFilePath; + + // TODO Refactor the `ParseSettingsFile(string) => Settings` method to `ParseSettingsFiles(string) => Hashtable`, and then remove + // the `return` statement in order to proceed to the call to `ParseSettingsHashtable(Hashtable) => Settings` on the result. ParseSettingsFile(settingsFilePath); + return; } - else - { - throw new ArgumentException( - String.Format( - CultureInfo.CurrentCulture, - Strings.InvalidPath, - settingsFilePath)); - } + + throw new ArgumentException(String.Format( + Strings.InvalidPath, + settingsFilePath)); } - else + + // Do the real work of parsing the `settings` Hashtable (whether passed directly or first parsed from a resolved file path). + var settingsHashtable = settings as Hashtable; + if (settingsHashtable != null) { - var settingsHashtable = settings as Hashtable; - if (settingsHashtable != null) - { - ParseSettingsHashtable(settingsHashtable); - } - else - { - throw new ArgumentException(Strings.SettingsInvalidType); - } + ParseSettingsHashtable(settingsHashtable); + return; } + + // The `settings` argument must be either a string or a Hashtable. + throw new ArgumentException(Strings.SettingsInvalidType); } /// From 25710dfe3bd9e8651c1552e364e2e92b973844a3 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Thu, 4 Jul 2019 20:34:04 -0500 Subject: [PATCH 74/75] Refactor `ParseSettingsFile(string) => void` to `static ParseSettingsFileToHashtable(string) => Hashtable` --- Engine/Settings.cs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 89a459ec4..770c29d4c 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -77,11 +77,7 @@ public Settings(object settings, Func presetResolver) if (File.Exists(settingsFilePath)) { this.filePath = settingsFilePath; - - // TODO Refactor the `ParseSettingsFile(string) => Settings` method to `ParseSettingsFiles(string) => Hashtable`, and then remove - // the `return` statement in order to proceed to the call to `ParseSettingsHashtable(Hashtable) => Settings` on the result. - ParseSettingsFile(settingsFilePath); - return; + settings = ParseSettingsFileToHashtable(settingsFilePath); } throw new ArgumentException(String.Format( @@ -557,7 +553,7 @@ private void ParseSettingsHashtable(Hashtable settings) } } - private void ParseSettingsFile(string settingsFilePath) + private static Hashtable ParseSettingsFileToHashtable(string settingsFilePath) { Token[] parserTokens = null; ParseError[] parserErrors = null; @@ -567,7 +563,9 @@ private void ParseSettingsFile(string settingsFilePath) // no hashtable, raise warning if (hashTableAsts.Count() == 0) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.InvalidProfile, settingsFilePath)); + throw new ArgumentException(string.Format( + Strings.InvalidProfile, + settingsFilePath)); } HashtableAst hashTableAst = hashTableAsts.First() as HashtableAst; @@ -578,21 +576,19 @@ private void ParseSettingsFile(string settingsFilePath) // it is not available on PSv3, we resort to our own narrow implementation. hashtable = GetSafeValueFromHashtableAst(hashTableAst); } - catch (InvalidOperationException e) + catch (InvalidOperationException exception) { - throw new ArgumentException(Strings.InvalidProfile, e); + throw new ArgumentException(Strings.InvalidProfile, exception); } - if (hashtable == null) + if (hashtable is null) { - throw new ArgumentException( - String.Format( - CultureInfo.CurrentCulture, - Strings.InvalidProfile, - settingsFilePath)); + throw new ArgumentException(String.Format( + Strings.InvalidProfile, + settingsFilePath)); } - ParseSettingsHashtable(hashtable); + return hashtable; } /// From 215f0e27b72905803732a920e3c469636c2299c9 Mon Sep 17 00:00:00 2001 From: "Travis C. LaGrone" <22419287+travis-c-lagrone@users.noreply.github.com> Date: Thu, 4 Jul 2019 20:43:43 -0500 Subject: [PATCH 75/75] Fix sequencing of invocation of `ParseSettingsFileToHashtable` in `Settings` constructor Fixes previous commit 25710df --- Engine/Settings.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Engine/Settings.cs b/Engine/Settings.cs index 770c29d4c..000bd9a35 100644 --- a/Engine/Settings.cs +++ b/Engine/Settings.cs @@ -74,15 +74,15 @@ public Settings(object settings, Func presetResolver) // Do not throw an exception if the `presetResolver` argument is null. This is because it is permitted for a file path `settings` to // not have any associated `presetResolver`. - if (File.Exists(settingsFilePath)) + if (!File.Exists(settingsFilePath)) { - this.filePath = settingsFilePath; - settings = ParseSettingsFileToHashtable(settingsFilePath); + throw new ArgumentException(String.Format( + Strings.InvalidPath, + settingsFilePath)); } - throw new ArgumentException(String.Format( - Strings.InvalidPath, - settingsFilePath)); + this.filePath = settingsFilePath; + settings = ParseSettingsFileToHashtable(settingsFilePath); } // Do the real work of parsing the `settings` Hashtable (whether passed directly or first parsed from a resolved file path).