Skip to content

Commit bc505d5

Browse files
committed
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.
1 parent 93b1813 commit bc505d5

21 files changed

Lines changed: 447 additions & 63 deletions

File tree

docfx/.editorconfig

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#Primary settings apply to all files unless overridden below
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 4
7+
insert_final_newline = true
8+
tab_width = 4
9+
end_of_line = crlf
10+
11+
# VSSPELL: Spell checker settings for all files
12+
vsspell_section_id = 869f688c36354e788f0a0b53ab42cf41
13+
14+
# [VSSpellChecker bug 277](https://github.com/EWSoftware/VSSpellChecker/issues/277)
15+
# VSSPELL: Disable the analyzers until Bug 277 is fixed.
16+
vsspell_code_analyzers_enabled = false
17+
vsspell_ignored_words_869f688c36354e788f0a0b53ab42cf41 = File:.\IgnoredWords.dic
18+
19+
# match VS generated formatting for MSBuild project files
20+
[*.*proj,*.props,*.targets]
21+
indent_style = space
22+
indent_size = 2
23+
tab_width = 2
24+
25+
[*.yml,*.yaml]
26+
indent_style = space
27+
indent_size = 2
28+
tab_width = 2
29+
30+
[*.md]
31+
# mark left margin for split screen preview of markdown files
32+
# requires: https://marketplace.visualstudio.com/items?itemName=PaulHarrington.EditorGuidelinesPreview
33+
guidelines = 92
34+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# UNC0000: An internal analyzer exception occurred.
2+
An internal error occurred in the analyzer. Please [report](https://github.com/UbiquityDotNET/Ubiquity.NET.Utils/issues)
3+
the issue with as much detail as possible, ideally with a small repro to help identify the
4+
problem. Please include the full stack of the exception as shown in the message details.
5+
Occurrences of this diagnostic are ALWAYS a bug in the generator/Analyzer in question and
6+
should be fixed.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# UNC001: Missing command attribute on containing type
2+
A command line property annotating attribute is applied to a property but the containing
3+
type does not contain an attribute that designates it as a command. The generator will
4+
***ignore*** the property and the ***entire*** class.
5+
6+
## Example:
7+
``` C#
8+
using System.IO;
9+
10+
using Ubiquity.NET.CommandLine.GeneratorAttributes;
11+
12+
namespace TestNamespace;
13+
14+
internal class testInput1
15+
{
16+
[Option( "-o" )] // UNC001 reported here
17+
public required DirectoryInfo SomePath { get; init; }
18+
}
19+
```
20+
21+
## Fix:
22+
``` C#
23+
using System.IO;
24+
25+
using Ubiquity.NET.CommandLine.GeneratorAttributes;
26+
27+
namespace TestNamespace;
28+
29+
// apply a command attribute to the type to allow generation
30+
// of the backing details for parsing and binding the results to this type.
31+
[RootCommand( Description = "Root command for tests" )]
32+
internal class testInput1
33+
{
34+
[Option( "-o" )]
35+
public required DirectoryInfo SomePath { get; init; }
36+
}
37+
```
38+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# UNC002 : Property attribute not allowed standalone
2+
This diagnostic is reported when an attribute is detected that is a qualifier to another
3+
required attribute, but the required attribute is not present.
4+
5+
## Example
6+
`FolderValidationAttribute` provides additional information for validation of a property. To
7+
generate source that operates correctly an additional attribute is required. For example,
8+
to specify that a Folder provided must exist already a command can apply the
9+
`FolderValidationAttribute`. However that attribute requires additional information to
10+
identify the property as an option and other behaviors. Without the constrained attribute
11+
this attribute and property it is attached to is ignored by source generation.
12+
13+
``` C#
14+
using System.IO;
15+
16+
using Ubiquity.NET.CommandLine.GeneratorAttributes;
17+
18+
namespace TestNamespace;
19+
20+
[RootCommand( Description = "Root command for tests" )]
21+
internal class testInput1
22+
{
23+
// This attribute triggers UNC002 - Property attribute FolderValidation is not allowed on a property independent of a qualifying attribute such as OptionAttribute.
24+
[FolderValidation( FolderValidation.CreateIfNotExist )]
25+
public required DirectoryInfo SomePath { get; init; }
26+
}
27+
```
28+
29+
## Fix
30+
``` C#
31+
using System.IO;
32+
33+
using Ubiquity.NET.CommandLine.GeneratorAttributes;
34+
35+
namespace TestNamespace;
36+
37+
[RootCommand( Description = "Root command for tests" )]
38+
internal class testInput1
39+
{
40+
// Fix is to apply the attribute that indicates the property being validated
41+
[Option( "-o", Description = "Test SomePath" )]
42+
[FolderValidation( FolderValidation.CreateIfNotExist )]
43+
public required DirectoryInfo SomePath { get; init; }
44+
}
45+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# UNC003: Property has incorrect type for attribute
2+
The property's type is not consistent with the requirements of the attribute applied to it.
3+
Some attributes (especially validation annotations) require a particular type for the
4+
property to generate valid code. When this diagnostic is raised it is an indication that an
5+
incorrect type is used. If this is ignored or suppressed the property is ignored for code
6+
generation.
7+
8+
## Example
9+
``` C#
10+
using System.IO;
11+
12+
using Ubiquity.NET.CommandLine.GeneratorAttributes;
13+
14+
namespace TestNamespace;
15+
16+
[RootCommand( Description = "Root command for tests" )]
17+
internal class testInput1
18+
{
19+
[Option( "-o", Description = "Test SomePath" )]
20+
// This attribute triggers UNC003 - Property attribute 'FileValidation' requires a property of type 'FileInfo'.
21+
[FileValidation( FileValidation.ExistingOnly )]
22+
public required string SomePath { get; init; }
23+
}
24+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# UNC004 : Property type is nullable but marked as required.
2+
This diagnostic is reported when a nullable type is marked as `Required`. This usually
3+
indicates an error in the source applying the attributes. An explicitly annotated nullable
4+
type has a legit value of null, therefore marking it as `Required` makes no sense. Required
5+
means that it is validated as specified on the command line, this validation only occurs at
6+
the time of ***invoking*** the command. (If a different command is parsed from the command
7+
line arguments then no validation occurs).
8+
9+
10+
## Example
11+
``` C#
12+
using Ubiquity.NET.CommandLine.GeneratorAttributes;
13+
14+
namespace TestNamespace;
15+
16+
[RootCommand( Description = "Root command for tests" )]
17+
internal partial class TestOptions
18+
{
19+
// Use of `Required = true` reports diagnostic; UNC004 : Property type is nullable but marked as required.
20+
[Option( "--thing1", Aliases = [ "-t" ], Required = true, Description = "Test Thing1", HelpName = "Help name for thing1" )]
21+
public bool? Thing1 { get; init; }
22+
23+
[Option( "--thing2", Aliases = [ "-t" ], Description = "Test Thing2", HelpName = "Help name for thing2" )]
24+
public bool Thing2 { get; init; }
25+
}
26+
```

docfx/CommandLine/index.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
11
# About
22
Ubiquity.NET.CommandLine contains general extensions for .NET. to support command line
3-
parsing using `System.CommandLine`
3+
parsing using `System.CommandLine`.
4+
5+
A source generator is included that will generate the boilerplate code for command line
6+
parsing and binding. Additionally an analyzer is provided to aid in identifying problems
7+
with usage of the attributes for generation.
8+
9+
## Analyzer Diagnostics
10+
Rule ID | Title |
11+
--------|-------|
12+
[UNC000](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC000.html) | An internal analyzer exception occurred. |
13+
[UNC001](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC001.html) | Missing command attribute on containing type. |
14+
[UNC002](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC002.html) | Property attribute not allowed standalone. |
15+
[UNC003](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC003.html) | Property has incorrect type for attribute. |
16+
[UNC004](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CommandLine/diagnostics/UNC004.html) | Property type is nullable but marked as required. |

docfx/IgnoredWords.dic

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nullable

docfx/documentation.msbuildproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
<Target Name="AlwaysRun" BeforeTargets="AfterBuild">
7575
<Message Importance="High" Text="NOTE: Building $(MSBuildProjectFile) does NOTHING, docs are built using the docfx tool. This project is simply a convenient placeholder for organizing/editing files" />
7676
</Target>
77+
7778
<!--
7879
Target to generate the versioning JSON file for consumption by the docs scripts. This target is explicitly called out
7980
by the build scripts to generate the JSON file with all the version details from Ubiquity.NET.Versioning.Build.Tasks.

src/Ubiquity.NET.CodeAnalysis.Utils/TypeSymbolExtensions.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public static class TypeSymbolExtensions
99
/// <summary>Gets the full name of the symbol (Including namespace names)</summary>
1010
/// <param name="self">Symbol to get the name from</param>
1111
/// <returns>Enumerable collection of the name parts with outer most namespace first</returns>
12-
public static IEnumerable<string> GetFullName( this ITypeSymbol self )
12+
public static IEnumerable<string> GetFullNameParts( this ITypeSymbol self )
1313
{
1414
foreach(string namespacePart in GetNamespaceNames( self ))
1515
{
@@ -19,6 +19,14 @@ public static IEnumerable<string> GetFullName( this ITypeSymbol self )
1919
yield return self.Name;
2020
}
2121

22+
/// <summary>Gets the fill name (including namespaces) of a <see cref="ITypeSymbol"/></summary>
23+
/// <param name="self"><see cref="ITypeSymbol"/> to get the name from</param>
24+
/// <returns>full name for the type</returns>
25+
public static string GetFullName(this ITypeSymbol self)
26+
{
27+
return string.Join(".", GetFullNameParts(self));
28+
}
29+
2230
/// <summary>Gets the sequence of namespaces names of the symbol (outermost to innermost)</summary>
2331
/// <param name="self">Symbol to get the name from</param>
2432
/// <returns>Enumerable collection of the name parts with outer most namespace first</returns>

0 commit comments

Comments
 (0)