diff --git a/docfx/CodeAnalysis/api/index.md b/docfx/CodeAnalysis/api/index.md index 194308b..7814826 100644 --- a/docfx/CodeAnalysis/api/index.md +++ b/docfx/CodeAnalysis/api/index.md @@ -1,6 +1,21 @@ # About -Ubiquity.NET.CodeAnalysis contains general extensions for .NET. +Ubiquity.NET.CodeAnlysis.Utils contains support for Roslyn components doing code analysis, +fixes, and source generation within the Roslyn compiler. ## Key support -TBD +* Support for caching of generation scan results (via `IEquatable`) +* Debug diagnostic asserts for class vs. struct trade-offs +* Capturing symbol information in a cacheable fashion +* Generation of diagnostics for issues detected in a generator1 +* Create a `SourceText` from a `StringBuilder` to allow generation to remain independent of + the Roslyn CodeAnalysis types. + +------ +1 Generators creating diagnostics is generally not recommended. +The official +[Incremental Generators](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md) +cook book recommends against it. A generator should generally ignore invalid input and fail +silently by ignoring the problem. An analyzer can produce the diagnostics for problems. At +most a generator can use this support to report critical problems that prevent the generator +from running at all. diff --git a/docfx/CodeAnalysis/index.md b/docfx/CodeAnalysis/index.md index c47a27e..5cacba3 100644 --- a/docfx/CodeAnalysis/index.md +++ b/docfx/CodeAnalysis/index.md @@ -1,4 +1,24 @@ # About -Ubiquity.NET.CodeAnlysis.Utils contains support for CodeAnalysis +The Ubiquity.NET.CodeAnalysis.Utils package contains support for building Roslyn analyzers, +fixers, and source generator. As such it specifically targets .NET Standard 2.0 and has no +dependencies on anything targeting a later runtime. If an analyzer (or any Roslyn extension) +depends on this package the dependencies for the package must be included in the extension's +package. ## Key support + +* Support for caching of generation scan results (via `IEquatable`) +* Debug diagnostic asserts for class vs. struct trade-offs +* Capturing symbol information in a cacheable fashion +* Generation of diagnostics for issues detected in a generator1 +* Create a `SourceText` from a `StringBuilder` to allow generation to remain independent of + the Roslyn CodeAnalysis types. + +------ +1 Generators creating diagnostics is generally not recommended. +The official +[Incremental Generators](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md) +cook book recommends against it. A generator should normally ignore invalid input and fail +silently by ignoring the problem. An analyzer can produce the diagnostics for problems. At +most a generator can use this support to report critical problems that prevent the generator +from running at all. diff --git a/docfx/IgnoredWords.dic b/docfx/IgnoredWords.dic index 8bdf685..25c4555 100644 --- a/docfx/IgnoredWords.dic +++ b/docfx/IgnoredWords.dic @@ -1,6 +1,7 @@ antlr arity bool +cacheable initializer interop marshalling diff --git a/docfx/documentation.msbuildproj b/docfx/documentation.msbuildproj index 928ad32..c9eaf24 100644 --- a/docfx/documentation.msbuildproj +++ b/docfx/documentation.msbuildproj @@ -65,12 +65,19 @@ - + + + + + + + + diff --git a/src/Ubiquity.NET.ANTLR.Utils/LocationExtensions.cs b/src/Ubiquity.NET.ANTLR.Utils/LocationExtensions.cs index de1ca1c..6ee1f0d 100644 --- a/src/Ubiquity.NET.ANTLR.Utils/LocationExtensions.cs +++ b/src/Ubiquity.NET.ANTLR.Utils/LocationExtensions.cs @@ -58,12 +58,10 @@ public static SourceRange GetSourceRange( this IToken token ) { ArgumentNullException.ThrowIfNull( token ); - // TODO: Q: Should this account for a newline in the token? - // A: Probably not, as a token can't span a newline. return new SourceRange( new(token.Line, token.Column, token.StartIndex), new(token.Line, token.Column + token.Text.Length, token.StopIndex) - ); + ); } } } diff --git a/src/Ubiquity.NET.ANTLR.Utils/ReadMe.md b/src/Ubiquity.NET.ANTLR.Utils/ReadMe.md index 7abac0e..555cee7 100644 --- a/src/Ubiquity.NET.ANTLR.Utils/ReadMe.md +++ b/src/Ubiquity.NET.ANTLR.Utils/ReadMe.md @@ -13,7 +13,7 @@ This library provides general extensions to ANTLR including adapter bindings for `Ubiquity.NET.Runtime.IParseErrorListener`. - This allows building consumers that deal with errors and remain independent of the parsing technology. -* Extension functions that provides commonly used support for ANTLR +* Extension functions that provide commonly used support for ANTLR - Get a character interval from a ParserRuleContext with support for the standard EOF rule. - Gets the source stream from an IRecognizer diff --git a/src/Ubiquity.NET.CodeAnalysis.Utils/DebugAssert.cs b/src/Ubiquity.NET.CodeAnalysis.Utils/DebugAssert.cs index 437316c..f3498d3 100644 --- a/src/Ubiquity.NET.CodeAnalysis.Utils/DebugAssert.cs +++ b/src/Ubiquity.NET.CodeAnalysis.Utils/DebugAssert.cs @@ -8,15 +8,15 @@ namespace Ubiquity.NET.CodeAnalysis.Utils /// Utility class to support Debug asserts public static class DebugAssert { - /// Tests if a structure size is < 16 bytes and generates a debug assertion if not + /// Tests if a structure size is < 16 bytes and generates a debug assertion if not /// Type of the struct to test /// /// This uses a runtime debug assert as it isn't possible to know the size at compile time of a managed struct. /// The `sizeof` doesn't apply for anything with a managed reference or a native pointer sized member /// as such sizes depend on the actual runtime used. /// - /// This function ONLY operates in a debug build. That is, this is the compiler will elide calls to this method - /// at the call site unless the "DEBUG" symbol is defined as it has a attached to it. + /// This function ONLY operates in a debug build. That is, the compiler will elide calls to this method + /// at the call site unless the "DEBUG" symbol is defined as it has a attached to it. /// /// /// diff --git a/src/Ubiquity.NET.CodeAnalysis.Utils/DiagnosticInfo.cs b/src/Ubiquity.NET.CodeAnalysis.Utils/DiagnosticInfo.cs index e9df540..f20aa1b 100644 --- a/src/Ubiquity.NET.CodeAnalysis.Utils/DiagnosticInfo.cs +++ b/src/Ubiquity.NET.CodeAnalysis.Utils/DiagnosticInfo.cs @@ -82,7 +82,7 @@ public override bool Equals( object obj ) /// public override int GetHashCode( ) { - // sadly this will re-hash the hashcode computed for the structure, but there is no way + // sadly this will re-hash the hash code computed for the structure, but there is no way // to combine the result of a hash with other things. (The overload of Add(int) is private) // The generic Add() will call the type's GetHashCode() and ignores the implementation of // IStructuralEquatable. diff --git a/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableAttributeData.cs b/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableAttributeData.cs index 5e2b718..b3c7707 100644 --- a/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableAttributeData.cs +++ b/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableAttributeData.cs @@ -44,7 +44,7 @@ public EquatableAttributeData( AttributeData data ) /// Gets the constant for a named argument /// Name of the argument to fetch - /// Optional value for the named argument ( is false if isn't provided) + /// Optional value for the named argument ( is false if isn't provided) public Optional GetNamedArgValue( string argName ) { return NamedArguments.TryGetValue( argName, out StructurallyEquatableTypedConstant typedConst ) @@ -76,7 +76,7 @@ public Optional GetNamedArgValue( string name ) /// Name of the attribute argument /// for the array of values /// - /// The name may not be specified in which case the result + /// The name may not be specified in which case the result /// will have not value ( is false). It is also /// possible that it was specified AND that the value is null (if T is a nullable type /// or a default instance if it is not.) Thus it is important to examine the return @@ -139,6 +139,8 @@ public override bool Equals( object obj ) && Equals( other ); } + /// Implicit cast from + /// Dat to convert [SuppressMessage( "Usage", "CA2225:Operator overloads have named alternates", Justification = "Implicit cast for public constructor" )] public static implicit operator EquatableAttributeData( AttributeData data ) { diff --git a/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableAttributeDataCollection.cs b/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableAttributeDataCollection.cs index 908038e..c890ffc 100644 --- a/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableAttributeDataCollection.cs +++ b/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableAttributeDataCollection.cs @@ -83,7 +83,7 @@ public override int GetHashCode( ) /// Gets a sequence of the names of all values. /// - /// These names are keys for the values used in and + /// These names are keys for the values used in and /// . /// public IEnumerable Keys => InnerDictionary.Keys; @@ -104,6 +104,8 @@ public bool TryGetValue( NamespaceQualifiedName key, [MaybeNullWhen( false )] ou return InnerDictionary.TryGetValue( key, out item ); } + /// Implicit cast from + /// Immutable array of attribute data to form a keyed collection from [SuppressMessage( "Usage", "CA2225:Operator overloads have named alternates", Justification = "Simple wrapper over public constructor" )] public static implicit operator EquatableAttributeDataCollection( ImmutableArray attributes ) { diff --git a/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableDictionary.cs b/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableDictionary.cs index c0ef644..996da03 100644 --- a/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableDictionary.cs +++ b/src/Ubiquity.NET.CodeAnalysis.Utils/EquatableDictionary.cs @@ -45,11 +45,19 @@ public bool Equals( EquatableDictionary other ) return ((IStructuralEquatable)this).Equals( other, EqualityComparer>.Default); } + /// Test two instances for equality + /// Left side of the comparison + /// Right side of the comparison + /// True if the values are equal and false if not public static bool operator ==( EquatableDictionary left, EquatableDictionary right ) { return left.Equals( right ); } + /// Test two instances for inequality + /// Left side of the comparison + /// Right side of the comparison + /// False if the values are not equal and true if not public static bool operator !=( EquatableDictionary left, EquatableDictionary right ) { return !(left == right); @@ -206,12 +214,16 @@ public bool TryGetValue( TKey key, out TValue value ) #endregion + /// Implicit cast from + /// Dictionary to wrap [SuppressMessage( "Usage", "CA2225:Operator overloads have named alternates", Justification = "Implicit cast for public constructor" )] public static implicit operator EquatableDictionary( ImmutableDictionary dictionaryToWrap ) { return new(dictionaryToWrap); } + /// Implicit cast from + /// Dictionary to wrap [SuppressMessage( "Usage", "CA2225:Operator overloads have named alternates", Justification = "Implicit cast for public constructor" )] public static implicit operator EquatableDictionary( ImmutableSortedDictionary dictionaryToWrap ) { diff --git a/src/Ubiquity.NET.CodeAnalysis.Utils/NamespaceQualifiedName.cs b/src/Ubiquity.NET.CodeAnalysis.Utils/NamespaceQualifiedName.cs index 446d577..893f563 100644 --- a/src/Ubiquity.NET.CodeAnalysis.Utils/NamespaceQualifiedName.cs +++ b/src/Ubiquity.NET.CodeAnalysis.Utils/NamespaceQualifiedName.cs @@ -176,7 +176,7 @@ public virtual string ToString( string format, IFormatProvider? formatProvider ) : customFormatter.Format(format, this, formatProvider); } - /// Implicit conversion to a string (Shorthand for calling + /// Implicit conversion to a string (Shorthand for calling /// Name to convert public static implicit operator string( NamespaceQualifiedName self ) { diff --git a/src/Ubiquity.NET.CodeAnalysis.Utils/ReadMe.md b/src/Ubiquity.NET.CodeAnalysis.Utils/ReadMe.md index b1afd13..c4ca0ac 100644 --- a/src/Ubiquity.NET.CodeAnalysis.Utils/ReadMe.md +++ b/src/Ubiquity.NET.CodeAnalysis.Utils/ReadMe.md @@ -4,3 +4,58 @@ and source generator. As such it specifically targets .NET Standard 2.0 and has dependencies on anything targeting a later runtime. If an analyzer (or any Roslyn extension) depends on this package the dependencies for the package must be included in the extension's package. + +## Key support + +* Support for caching of generation scan results (via `IEquatable`) +* Debug diagnostic asserts for class vs. struct trade-offs +* Capturing symbol information in a cacheable fashion +* Generation of diagnostics for issues detected in a generator1 +* Create a `SourceText` from a `StringBuilder` to allow generation to remain independent of + the Roslyn CodeAnalysis types. + +## Dependencies +| Name | Version | Description | +|------|---------|-------------| +| Microsoft.Bcl.HashCode | 6.0.0 | Poly fill library for HashCode support | +| Microsoft.CodeAnalysis.Analyzers | 4.14.0 | Roslyn Analyzer library | +| Microsoft.CodeAnalysis.CSharp | 5.0.0 | Roslyn library specifically for C# | + +Microsoft.CodeAnalysis.* is assumed already present for a Roslyn component and therefor is +not required in the package. + +Full documentation of the support provided by this package is available in the online +[documentation](https://ubiquitydotnet.github.io/Ubiquity.NET.Utils/CodeAnalysis/index.html). + +### Example of use + +``` XML + + + + + + <__AssemblyPath>$(PkgMicrosoft_Bcl_HashCode)\lib\$(TargetFramework)\*.dll + + + + + + + + + + + + + +``` + +------ +1 Generators creating diagnostics is generally not recommended. +The official +[Incremental Generators](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md) +cook book recommends against it. A generator should normally ignore invalid input and fail +silently by ignoring the problem. An analyzer can produce the diagnostics for problems. At +most a generator can use this support to report critical problems that prevent the generator +from running at all. diff --git a/src/Ubiquity.NET.CodeAnalysis.Utils/StructurallyEquatableTypedConstant.cs b/src/Ubiquity.NET.CodeAnalysis.Utils/StructurallyEquatableTypedConstant.cs index 5af2580..b954b54 100644 --- a/src/Ubiquity.NET.CodeAnalysis.Utils/StructurallyEquatableTypedConstant.cs +++ b/src/Ubiquity.NET.CodeAnalysis.Utils/StructurallyEquatableTypedConstant.cs @@ -64,11 +64,19 @@ public override int GetHashCode( ) return StructuralTypedConstantComparer.Default.GetHashCode(InnerConst); } + /// Test two instances for equality + /// Left side of the comparison + /// Right side of the comparison + /// True if the values are equal and false if not public static bool operator ==( StructurallyEquatableTypedConstant left, StructurallyEquatableTypedConstant right ) { return left.Equals(right); } + /// Test two instances for inequality + /// Left side of the comparison + /// Right side of the comparison + /// False if the values are not equal and true if not public static bool operator !=( StructurallyEquatableTypedConstant left, StructurallyEquatableTypedConstant right ) { return !(left == right); @@ -87,12 +95,16 @@ public TypedConstant ToTypedConstant() private readonly TypedConstant InnerConst; + /// Implicit cast from + /// Constant to cast [SuppressMessage( "Usage", "CA2225:Operator overloads have named alternates", Justification = "Simple alternate for existing constructor" )] public static implicit operator StructurallyEquatableTypedConstant( TypedConstant other ) { return new( other ); } + /// Implicit cast from + /// Constant to cast public static explicit operator TypedConstant( StructurallyEquatableTypedConstant other ) { return other.ToTypedConstant(); diff --git a/src/Ubiquity.NET.CodeAnalysis.Utils/Ubiquity.NET.CodeAnalysis.Utils.csproj b/src/Ubiquity.NET.CodeAnalysis.Utils/Ubiquity.NET.CodeAnalysis.Utils.csproj index 2a6bffe..2117c32 100644 --- a/src/Ubiquity.NET.CodeAnalysis.Utils/Ubiquity.NET.CodeAnalysis.Utils.csproj +++ b/src/Ubiquity.NET.CodeAnalysis.Utils/Ubiquity.NET.CodeAnalysis.Utils.csproj @@ -10,6 +10,7 @@ errors in most cases, but sometimes can end up as runtime errors! BEWARE! --> 12 + True true @@ -26,7 +27,6 @@ true snupkg True - diff --git a/src/Ubiquity.NET.CommandLine.SrcGen/CommandLineAnalyzer.cs b/src/Ubiquity.NET.CommandLine.SrcGen/CommandLineAnalyzer.cs index 7f7f72c..0d1809e 100644 --- a/src/Ubiquity.NET.CommandLine.SrcGen/CommandLineAnalyzer.cs +++ b/src/Ubiquity.NET.CommandLine.SrcGen/CommandLineAnalyzer.cs @@ -99,9 +99,6 @@ private static void OnProperty( SymbolAnalysisContext context ) handler( context, propSymbol, attrib.GetLocation(), attribs ); } } - - // TODO: WARN if type of property is nullable AND marked as required. That won't produce an error but probably - // not the behavior intended... } private static void OnOptionAttribute( @@ -204,7 +201,7 @@ string typeConstraintName context.ReportDiagnostic( Diagnostics.MissingConstraintAttribute( attribLoc, typeConstraintName ) ); } - // TODO: validate an Argument attribute or Option attribute + // TODO: validate an Argument attribute OR Option attribute } /// Verifies a property has an expected type diff --git a/src/Ubiquity.NET.CommandLine.SrcGen/CommandLinePolyFill.cs b/src/Ubiquity.NET.CommandLine.SrcGen/CommandLinePolyFill.cs index 0baf59b..6bfd6ab 100644 --- a/src/Ubiquity.NET.CommandLine.SrcGen/CommandLinePolyFill.cs +++ b/src/Ubiquity.NET.CommandLine.SrcGen/CommandLinePolyFill.cs @@ -7,7 +7,7 @@ namespace Ubiquity.NET.CommandLine.SrcGen // only supports .NET 8.0 and .NET 10. So this fills in the gaps on enumerations as the // library with the declarations can't be referenced directly. - /// Flags to determine the default Options for an + /// Flags to determine the default Options for an AppControlledDefaultsRootCommand [Flags] internal enum DefaultOption { @@ -21,20 +21,20 @@ internal enum DefaultOption Version, } - /// Flags to determine the default directives supported for an + /// Flags to determine the default directives supported for an AppControlledDefaultsRootCommand [Flags] internal enum DefaultDirective { /// No default directives included None = 0, - /// Include support for + /// Include support for SuggestDirective Suggest, - /// Include support for + /// Include support for DiagramDirective Diagram, - /// Include support for + /// Include support for EnvironmentVariablesDirective EnvironmentVariables, } diff --git a/src/Ubiquity.NET.CommandLine.SrcGen/Ubiquity.NET.CommandLine.SrcGen.csproj b/src/Ubiquity.NET.CommandLine.SrcGen/Ubiquity.NET.CommandLine.SrcGen.csproj index 6aceaf7..62d24c2 100644 --- a/src/Ubiquity.NET.CommandLine.SrcGen/Ubiquity.NET.CommandLine.SrcGen.csproj +++ b/src/Ubiquity.NET.CommandLine.SrcGen/Ubiquity.NET.CommandLine.SrcGen.csproj @@ -16,6 +16,7 @@ false true True + True true diff --git a/src/Ubiquity.NET.CommandLine/GeneratorAttributes/CommandAttribute.cs b/src/Ubiquity.NET.CommandLine/GeneratorAttributes/CommandAttribute.cs index 75c7eda..76c4175 100644 --- a/src/Ubiquity.NET.CommandLine/GeneratorAttributes/CommandAttribute.cs +++ b/src/Ubiquity.NET.CommandLine/GeneratorAttributes/CommandAttribute.cs @@ -3,6 +3,8 @@ namespace Ubiquity.NET.CommandLine.GeneratorAttributes { +// Feature not supported yet +#if SUPPORT_SUB_COMMANDS /// Attribute to mark a command for generation of the backing implementation [AttributeUsage( AttributeTargets.Class, Inherited = false, AllowMultiple = false )] public sealed class CommandAttribute @@ -18,4 +20,5 @@ public CommandAttribute( string? description = null ) /// Gets the Description for this command public string Description { get; } } +#endif } diff --git a/src/Ubiquity.NET.SourceGenerator.Test.Utils/Ubiquity.NET.SourceGenerator.Test.Utils.csproj b/src/Ubiquity.NET.SourceGenerator.Test.Utils/Ubiquity.NET.SourceGenerator.Test.Utils.csproj index f423851..410325a 100644 --- a/src/Ubiquity.NET.SourceGenerator.Test.Utils/Ubiquity.NET.SourceGenerator.Test.Utils.csproj +++ b/src/Ubiquity.NET.SourceGenerator.Test.Utils/Ubiquity.NET.SourceGenerator.Test.Utils.csproj @@ -3,6 +3,7 @@ net10.0 enable + True true @@ -18,7 +19,6 @@ Apache-2.0 WITH LLVM-exception true snupkg - True