From bc505d5fcb32333402d17833623ff471de459633 Mon Sep 17 00:00:00 2001 From: Steven Maillet Date: Mon, 19 Jan 2026 10:49:48 -0800 Subject: [PATCH] Added test for properties with a nullable type that are marked as required. * This is, at best, a pointless NOP. - Behavior of this is formally UNDEFINED. - Generate a warning diagnostic to verify that * Added documentation for analyzer diagnostics - This is published with the documentation for the library - Published location is the HelpURI for the diagnostic to allow click through to the helps from an IDE. --- docfx/.editorconfig | 34 +++++++++ docfx/CommandLine/Diagnostics/UNC000.md | 6 ++ docfx/CommandLine/Diagnostics/UNC001.md | 38 ++++++++++ docfx/CommandLine/Diagnostics/UNC002.md | 45 +++++++++++ docfx/CommandLine/Diagnostics/UNC003.md | 24 ++++++ docfx/CommandLine/Diagnostics/UNC004.md | 26 +++++++ docfx/CommandLine/index.md | 15 +++- docfx/IgnoredWords.dic | 1 + docfx/documentation.msbuildproj | 1 + .../TypeSymbolExtensions.cs | 10 ++- .../CommandAnalyzerTests.cs | 76 ++++++++++++++++--- .../input.cs | 4 +- .../input.cs | 11 +++ .../input.cs | 7 +- .../input.cs | 14 ++++ .../Ubiquity.NET.CommandLine.SrcGen.UT.csproj | 4 + .../AnalyzerReleases.Unshipped.md | 5 +- .../CommandLineAnalyzer.cs | 69 +++++++++++++---- .../Diagnostics.cs | 28 ++++++- .../Properties/Resources.Designer.cs | 27 +++++++ .../Properties/Resources.resx | 65 +++++++++------- 21 files changed, 447 insertions(+), 63 deletions(-) create mode 100644 docfx/.editorconfig create mode 100644 docfx/CommandLine/Diagnostics/UNC000.md create mode 100644 docfx/CommandLine/Diagnostics/UNC001.md create mode 100644 docfx/CommandLine/Diagnostics/UNC002.md create mode 100644 docfx/CommandLine/Diagnostics/UNC003.md create mode 100644 docfx/CommandLine/Diagnostics/UNC004.md create mode 100644 docfx/IgnoredWords.dic create mode 100644 src/Ubiquity.NET.CommandLine.SrcGen.UT/TestFiles/CommandAnalyzerTests/FileValidation_with_wrong_type_produces_UNC002/input.cs create mode 100644 src/Ubiquity.NET.CommandLine.SrcGen.UT/TestFiles/CommandAnalyzerTests/Required_nullable_types_produce_diagnostic/input.cs diff --git a/docfx/.editorconfig b/docfx/.editorconfig new file mode 100644 index 0000000..2364720 --- /dev/null +++ b/docfx/.editorconfig @@ -0,0 +1,34 @@ +#Primary settings apply to all files unless overridden below +root = true + +[*] +indent_style = space +indent_size = 4 +insert_final_newline = true +tab_width = 4 +end_of_line = crlf + +# VSSPELL: Spell checker settings for all files +vsspell_section_id = 869f688c36354e788f0a0b53ab42cf41 + +# [VSSpellChecker bug 277](https://github.com/EWSoftware/VSSpellChecker/issues/277) +# VSSPELL: Disable the analyzers until Bug 277 is fixed. +vsspell_code_analyzers_enabled = false +vsspell_ignored_words_869f688c36354e788f0a0b53ab42cf41 = File:.\IgnoredWords.dic + +# match VS generated formatting for MSBuild project files +[*.*proj,*.props,*.targets] +indent_style = space +indent_size = 2 +tab_width = 2 + +[*.yml,*.yaml] +indent_style = space +indent_size = 2 +tab_width = 2 + +[*.md] +# mark left margin for split screen preview of markdown files +# requires: https://marketplace.visualstudio.com/items?itemName=PaulHarrington.EditorGuidelinesPreview +guidelines = 92 + diff --git a/docfx/CommandLine/Diagnostics/UNC000.md b/docfx/CommandLine/Diagnostics/UNC000.md new file mode 100644 index 0000000..cab668f --- /dev/null +++ b/docfx/CommandLine/Diagnostics/UNC000.md @@ -0,0 +1,6 @@ +# UNC0000: An internal analyzer exception occurred. +An internal error occurred in the analyzer. Please [report](https://github.com/UbiquityDotNET/Ubiquity.NET.Utils/issues) +the issue with as much detail as possible, ideally with a small repro to help identify the +problem. Please include the full stack of the exception as shown in the message details. +Occurrences of this diagnostic are ALWAYS a bug in the generator/Analyzer in question and +should be fixed. diff --git a/docfx/CommandLine/Diagnostics/UNC001.md b/docfx/CommandLine/Diagnostics/UNC001.md new file mode 100644 index 0000000..3eccf60 --- /dev/null +++ b/docfx/CommandLine/Diagnostics/UNC001.md @@ -0,0 +1,38 @@ +# UNC001: Missing command attribute on containing type +A command line property annotating attribute is applied to a property but the containing +type does not contain an attribute that designates it as a command. The generator will +***ignore*** the property and the ***entire*** class. + +## Example: +``` C# +using System.IO; + +using Ubiquity.NET.CommandLine.GeneratorAttributes; + +namespace TestNamespace; + +internal class testInput1 +{ + [Option( "-o" )] // UNC001 reported here + public required DirectoryInfo SomePath { get; init; } +} +``` + +## Fix: +``` C# +using System.IO; + +using Ubiquity.NET.CommandLine.GeneratorAttributes; + +namespace TestNamespace; + +// apply a command attribute to the type to allow generation +// of the backing details for parsing and binding the results to this type. +[RootCommand( Description = "Root command for tests" )] +internal class testInput1 +{ + [Option( "-o" )] + public required DirectoryInfo SomePath { get; init; } +} +``` + diff --git a/docfx/CommandLine/Diagnostics/UNC002.md b/docfx/CommandLine/Diagnostics/UNC002.md new file mode 100644 index 0000000..aebdbcc --- /dev/null +++ b/docfx/CommandLine/Diagnostics/UNC002.md @@ -0,0 +1,45 @@ +# UNC002 : Property attribute not allowed standalone +This diagnostic is reported when an attribute is detected that is a qualifier to another +required attribute, but the required attribute is not present. + +## Example +`FolderValidationAttribute` provides additional information for validation of a property. To +generate source that operates correctly an additional attribute is required. For example, +to specify that a Folder provided must exist already a command can apply the +`FolderValidationAttribute`. However that attribute requires additional information to +identify the property as an option and other behaviors. Without the constrained attribute +this attribute and property it is attached to is ignored by source generation. + +``` C# +using System.IO; + +using Ubiquity.NET.CommandLine.GeneratorAttributes; + +namespace TestNamespace; + +[RootCommand( Description = "Root command for tests" )] +internal class testInput1 +{ + // This attribute triggers UNC002 - Property attribute FolderValidation is not allowed on a property independent of a qualifying attribute such as OptionAttribute. + [FolderValidation( FolderValidation.CreateIfNotExist )] + public required DirectoryInfo SomePath { get; init; } +} +``` + +## Fix +``` C# +using System.IO; + +using Ubiquity.NET.CommandLine.GeneratorAttributes; + +namespace TestNamespace; + +[RootCommand( Description = "Root command for tests" )] +internal class testInput1 +{ + // Fix is to apply the attribute that indicates the property being validated + [Option( "-o", Description = "Test SomePath" )] + [FolderValidation( FolderValidation.CreateIfNotExist )] + public required DirectoryInfo SomePath { get; init; } +} +``` diff --git a/docfx/CommandLine/Diagnostics/UNC003.md b/docfx/CommandLine/Diagnostics/UNC003.md new file mode 100644 index 0000000..d3e4ba5 --- /dev/null +++ b/docfx/CommandLine/Diagnostics/UNC003.md @@ -0,0 +1,24 @@ +# UNC003: Property has incorrect type for attribute +The property's type is not consistent with the requirements of the attribute applied to it. +Some attributes (especially validation annotations) require a particular type for the +property to generate valid code. When this diagnostic is raised it is an indication that an +incorrect type is used. If this is ignored or suppressed the property is ignored for code +generation. + +## Example +``` C# +using System.IO; + +using Ubiquity.NET.CommandLine.GeneratorAttributes; + +namespace TestNamespace; + +[RootCommand( Description = "Root command for tests" )] +internal class testInput1 +{ + [Option( "-o", Description = "Test SomePath" )] + // This attribute triggers UNC003 - Property attribute 'FileValidation' requires a property of type 'FileInfo'. + [FileValidation( FileValidation.ExistingOnly )] + public required string SomePath { get; init; } +} +``` diff --git a/docfx/CommandLine/Diagnostics/UNC004.md b/docfx/CommandLine/Diagnostics/UNC004.md new file mode 100644 index 0000000..edaca69 --- /dev/null +++ b/docfx/CommandLine/Diagnostics/UNC004.md @@ -0,0 +1,26 @@ +# UNC004 : Property type is nullable but marked as required. +This diagnostic is reported when a nullable type is marked as `Required`. This usually +indicates an error in the source applying the attributes. An explicitly annotated nullable +type has a legit value of null, therefore marking it as `Required` makes no sense. Required +means that it is validated as specified on the command line, this validation only occurs at +the time of ***invoking*** the command. (If a different command is parsed from the command +line arguments then no validation occurs). + + +## Example +``` C# +using Ubiquity.NET.CommandLine.GeneratorAttributes; + +namespace TestNamespace; + +[RootCommand( Description = "Root command for tests" )] +internal partial class TestOptions +{ + // Use of `Required = true` reports diagnostic; UNC004 : Property type is nullable but marked as required. + [Option( "--thing1", Aliases = [ "-t" ], Required = true, Description = "Test Thing1", HelpName = "Help name for thing1" )] + public bool? Thing1 { get; init; } + + [Option( "--thing2", Aliases = [ "-t" ], Description = "Test Thing2", HelpName = "Help name for thing2" )] + public bool Thing2 { get; init; } +} +``` diff --git a/docfx/CommandLine/index.md b/docfx/CommandLine/index.md index e35c04e..3395438 100644 --- a/docfx/CommandLine/index.md +++ b/docfx/CommandLine/index.md @@ -1,3 +1,16 @@ # About Ubiquity.NET.CommandLine contains general extensions for .NET. to support command line -parsing using `System.CommandLine` +parsing using `System.CommandLine`. + +A source generator is included that will generate the boilerplate code for command line +parsing and binding. Additionally an analyzer is provided to aid in identifying problems +with usage of the attributes for generation. + +## Analyzer Diagnostics +Rule ID | Title | +--------|-------| +[UNC000](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC000.html) | An internal analyzer exception occurred. | +[UNC001](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC001.html) | Missing command attribute on containing type. | +[UNC002](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC002.html) | Property attribute not allowed standalone. | +[UNC003](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC003.html) | Property has incorrect type for attribute. | +[UNC004](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC004.html) | Property type is nullable but marked as required. | diff --git a/docfx/IgnoredWords.dic b/docfx/IgnoredWords.dic new file mode 100644 index 0000000..35bce0a --- /dev/null +++ b/docfx/IgnoredWords.dic @@ -0,0 +1 @@ +nullable diff --git a/docfx/documentation.msbuildproj b/docfx/documentation.msbuildproj index 8ca8982..928ad32 100644 --- a/docfx/documentation.msbuildproj +++ b/docfx/documentation.msbuildproj @@ -74,6 +74,7 @@ + @@ -154,4 +154,13 @@ Property has incorrect type for attribute - \ No newline at end of file + + Property type is nullable but marked as required; These annotations conflict resulting in behavior that is explicitly UNDEFINED. + + + Type '{0}' for property '{1}' is nullable but marked as required; These annotations conflict resulting in behavior that is explicitly UNDEFINED. + + + Property type is nullable but marked as required. + +