diff --git a/Directory.Build.props b/Directory.Build.props index 05cf2ff..d644ab0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - - true - enable - - \ No newline at end of file + + true + enable + + diff --git a/Directory.Build.targets b/Directory.Build.targets index 6f02e62..63a3535 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,18 +1,21 @@ - - - - <_ExcludePackage Include="@(PackageRedirect)" Exclude="@(PackageReference)"> - PackageName - %(PackageName) - - <_ExcludeProjects Include ="@(_ExcludePackage->Metadata('ProjectPath'))" /> - - %(PackageRedirect.ReferenceOutputAssembly) - %(PackageRedirect.ItemOutputGroup) - + <_ExcludePackage Include="@(PackageRedirect)" Exclude="@(PackageReference)"> + PackageName + %(PackageName) + + <_ExcludeProjects Include="@(_ExcludePackage->Metadata('ProjectPath'))" /> + + %(PackageRedirect.ReferenceOutputAssembly) + %(PackageRedirect.ItemOutputGroup) + - - - \ No newline at end of file + + + diff --git a/nuget.config b/nuget.config index 1f7ac81..146b8ac 100644 --- a/nuget.config +++ b/nuget.config @@ -3,7 +3,10 @@ - + diff --git a/src/Async/StaticCs.Async.csproj b/src/Async/StaticCs.Async.csproj index 713c239..2b5ed7b 100644 --- a/src/Async/StaticCs.Async.csproj +++ b/src/Async/StaticCs.Async.csproj @@ -1,5 +1,4 @@  - netstandard2.0 enable @@ -18,6 +17,4 @@ - - diff --git a/src/Async/TaskScope.cs b/src/Async/TaskScope.cs index 4c3bc8b..0974fbb 100644 --- a/src/Async/TaskScope.cs +++ b/src/Async/TaskScope.cs @@ -1,4 +1,3 @@ - using System; using System.Collections.Generic; using System.Diagnostics; @@ -14,7 +13,10 @@ public sealed class TaskScope private TaskScope() { } - public static async Task With(Func action, Action? onCanceled = null) + public static async Task With( + Func action, + Action? onCanceled = null + ) { var scope = new TaskScope(); try @@ -37,7 +39,7 @@ public static async Task With(Func action, Action action, Action? unhandledCancels = null; - foreach (var ex in (IEnumerable?)t.Exception?.InnerExceptions ?? Array.Empty()) + foreach ( + var ex in (IEnumerable?)t.Exception?.InnerExceptions + ?? Array.Empty() + ) { // Filter out all cancellations that are owned by this scope if (ex is OperationCanceledException e && e.CancellationToken != _cts.Token) @@ -130,18 +135,23 @@ public Task Run(Func action) throw new InvalidOperationException("TaskScope has ended."); } - var task = Task.Factory.StartNew(async () => - { - try - { - await action(); - } - catch when (CancelAndRethrow(this)) - { - throw new Exception("Unreachable"); - } - }, TaskCreationOptions.AttachedToParent).Unwrap(); + var task = Task + .Factory.StartNew( + async () => + { + try + { + await action(); + } + catch when (CancelAndRethrow(this)) + { + throw new Exception("Unreachable"); + } + }, + TaskCreationOptions.AttachedToParent + ) + .Unwrap(); _tasks.Add(task); return task; } -} \ No newline at end of file +} diff --git a/src/Collections/EnumerableEx.cs b/src/Collections/EnumerableEx.cs index 2522048..7263181 100644 --- a/src/Collections/EnumerableEx.cs +++ b/src/Collections/EnumerableEx.cs @@ -38,4 +38,4 @@ public static class EnumerableEx2 return null; } -} \ No newline at end of file +} diff --git a/src/Collections/EqArray.cs b/src/Collections/EqArray.cs index da1723e..708265f 100644 --- a/src/Collections/EqArray.cs +++ b/src/Collections/EqArray.cs @@ -1,4 +1,3 @@ - using System; using System.Collections; using System.Collections.Generic; @@ -12,8 +11,11 @@ namespace StaticCs.Collections; public static class EqArray { public static EqArray ToEq(this IEnumerable array) => new(array.ToImmutableArray()); + public static EqArray ToEq(this ImmutableArray array) => new(array); + public static EqArray Create(params T[] array) => ImmutableArray.Create(array).ToEq(); + public static EqArray Create(ReadOnlySpan span) => ImmutableArray.Create(span).ToEq(); } @@ -24,6 +26,7 @@ public static class EqArray public readonly struct EqArray : IReadOnlyCollection, IEquatable> { private readonly ImmutableArray value; + public EqArray(ImmutableArray value) { this.value = value; @@ -72,22 +75,26 @@ public readonly record struct SerializeImpl(EqArray Value) : ISeria { public void Serialize(ISerializer serializer) { - EnumerableHelpers.SerializeSpan(typeof(EqArray).Name, Value.Array.AsSpan(), serializer); + EnumerableHelpers.SerializeSpan( + typeof(EqArray).Name, + Value.Array.AsSpan(), + serializer + ); } } + public readonly record struct DeserializeImpl : IDeserialize> where TWrap : IDeserialize { static EqArray IDeserialize>.Deserialize(ref D deserializer) { - return deserializer.DeserializeEnumerable< - EqArray, - Visitor>(new Visitor()); + return deserializer.DeserializeEnumerable, Visitor>(new Visitor()); } private struct Visitor : IDeserializeVisitor> { public string ExpectedTypeName => typeof(ImmutableArray).ToString(); + EqArray IDeserializeVisitor>.VisitEnumerable(ref D d) { ImmutableArray.Builder builder; @@ -107,7 +114,9 @@ EqArray IDeserializeVisitor>.VisitEnumerable(ref D d) } if (size >= 0 && builder.Count != size) { - throw new InvalidDeserializeValueException($"Expected {size} items, found {builder.Count}"); + throw new InvalidDeserializeValueException( + $"Expected {size} items, found {builder.Count}" + ); } return new(builder.ToImmutable()); } diff --git a/src/Collections/StaticCs.Collections.csproj b/src/Collections/StaticCs.Collections.csproj index af3bb70..3464a6a 100644 --- a/src/Collections/StaticCs.Collections.csproj +++ b/src/Collections/StaticCs.Collections.csproj @@ -1,5 +1,4 @@ - net7.0;net8.0 1.1.0 @@ -15,5 +14,4 @@ - diff --git a/src/IndentingBuilder/IndentingBuilder.cs b/src/IndentingBuilder/IndentingBuilder.cs index 40eab79..81b9215 100644 --- a/src/IndentingBuilder/IndentingBuilder.cs +++ b/src/IndentingBuilder/IndentingBuilder.cs @@ -1,243 +1,279 @@ +using System; using System.Runtime.CompilerServices; using System.Text; -namespace StaticCs; - -/// -/// A string builder with automatic indentation support for multi-line text. -/// Manages indentation levels with and methods, -/// and automatically applies the current indentation to appended content and interpolated strings. -/// -public sealed class IndentingBuilder : IComparable, IEquatable +#if !NET6_0_OR_GREATER +namespace System.Runtime.CompilerServices { - public static readonly Encoding UTF8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); - - private string _currentIndentWhitespace = ""; - private StringBuilder _stringBuilder; + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Struct, + AllowMultiple = false, + Inherited = false + )] + internal sealed class InterpolatedStringHandlerAttribute : Attribute { } - public IndentingBuilder(string s) + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] + internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute { - _stringBuilder = new StringBuilder(s); - } + public InterpolatedStringHandlerArgumentAttribute(string argument) + { + Arguments = new string[] { argument }; + } - public IndentingBuilder(SourceBuilderStringHandler s) - { - _currentIndentWhitespace = ""; - _stringBuilder = s._stringBuilder; - } + public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) + { + Arguments = arguments; + } - public IndentingBuilder() - { - _stringBuilder = new StringBuilder(); + public string[] Arguments { get; } } +} +#endif +namespace StaticCs +{ /// - /// Removes trailing whitespace from every line and replace all newlines with - /// Environment.NewLine. + /// A string builder with automatic indentation support for multi-line text. + /// Manages indentation levels with and methods, + /// and automatically applies the current indentation to appended content and interpolated strings. /// - private void Normalize() + public sealed class IndentingBuilder + : IComparable, + IEquatable { - _stringBuilder.Replace("\r\n", "\n"); + public static readonly Encoding UTF8Encoding = new UTF8Encoding( + encoderShouldEmitUTF8Identifier: false + ); + + private string _currentIndentWhitespace = ""; + private StringBuilder _stringBuilder; + + public IndentingBuilder(string s) + { + _stringBuilder = new StringBuilder(s); + } + + public IndentingBuilder(SourceBuilderStringHandler s) + { + _currentIndentWhitespace = ""; + _stringBuilder = s._stringBuilder; + } + + public IndentingBuilder() + { + _stringBuilder = new StringBuilder(); + } - // Remove trailing whitespace from every line - int wsStart; - for (int i = 0; i < _stringBuilder.Length; i++) + /// + /// Removes trailing whitespace from every line and replace all newlines with + /// Environment.NewLine. + /// + private void Normalize() { - if (_stringBuilder[i] is '\n') + _stringBuilder.Replace("\r\n", "\n"); + + // Remove trailing whitespace from every line + int wsStart; + for (int i = 0; i < _stringBuilder.Length; i++) { - wsStart = i - 1; - while (wsStart >= 0 && (_stringBuilder[wsStart] is ' ' or '\t')) - { - wsStart--; - } - wsStart++; // Move back to first whitespace - if (wsStart < i) + if (_stringBuilder[i] is '\n') { - int len = i - wsStart; - _stringBuilder.Remove(wsStart, len); - i -= len; + wsStart = i - 1; + while (wsStart >= 0 && (_stringBuilder[wsStart] is ' ' or '\t')) + { + wsStart--; + } + wsStart++; // Move back to first whitespace + if (wsStart < i) + { + int len = i - wsStart; + _stringBuilder.Remove(wsStart, len); + i -= len; + } } } - } - _stringBuilder.Replace("\n", Environment.NewLine); - } + _stringBuilder.Replace("\n", Environment.NewLine); + } - public override string ToString() - { - Normalize(); - return _stringBuilder.ToString(); - } + public override string ToString() + { + Normalize(); + return _stringBuilder.ToString(); + } - public void Append( - [InterpolatedStringHandlerArgument("")] - SourceBuilderStringHandler s) - { - // No work needed, the handler has already added the text to the string builder - } + public void Append([InterpolatedStringHandlerArgument("")] SourceBuilderStringHandler s) + { + // No work needed, the handler has already added the text to the string builder + } - public void Append(string s) - { - _stringBuilder.Append(_currentIndentWhitespace); - Append(_stringBuilder, _currentIndentWhitespace, s); - } + public void Append(string s) + { + _stringBuilder.Append(_currentIndentWhitespace); + Append(_stringBuilder, _currentIndentWhitespace, s); + } - public void Append(IndentingBuilder srcBuilder) - { - Append(srcBuilder.ToString()); - } + public void Append(IndentingBuilder srcBuilder) + { + Append(srcBuilder.ToString()); + } - private static void Append( - StringBuilder builder, - string currentIndentWhitespace, - string str) - { - int start = 0; - int nl; - while (start < str.Length) + private static void Append( + StringBuilder builder, + string currentIndentWhitespace, + string str + ) { - nl = str.IndexOf('\n', start); - if (nl == -1) + int start = 0; + int nl; + while (start < str.Length) { - nl = str.Length; - } - // Skip blank lines - while (nl < str.Length && (str[nl] == '\n' || str[nl] == '\r')) - { - nl++; - } - if (start > 0) - { - builder.Append(currentIndentWhitespace); + nl = str.IndexOf('\n', start); + if (nl == -1) + { + nl = str.Length; + } + // Skip blank lines + while (nl < str.Length && (str[nl] == '\n' || str[nl] == '\r')) + { + nl++; + } + if (start > 0) + { + builder.Append(currentIndentWhitespace); + } + builder.Append(str, start, nl - start); + start = nl; } - builder.Append(str, start, nl - start); - start = nl; } - } - - public void AppendLine( - [InterpolatedStringHandlerArgument("")] - SourceBuilderStringHandler s) - { - Append(s); - _stringBuilder.AppendLine(); - } - - public void AppendLine(string s) - { - Append(s); - _stringBuilder.AppendLine(); - } - public int CompareTo(IndentingBuilder? other) - { - if (other is null) return 1; + public void AppendLine([InterpolatedStringHandlerArgument("")] SourceBuilderStringHandler s) + { + Append(s); + _stringBuilder.AppendLine(); + } - var lenCmp = _stringBuilder.Length.CompareTo(other._stringBuilder.Length); - if (lenCmp != 0) + public void AppendLine(string s) { - return lenCmp; + Append(s); + _stringBuilder.AppendLine(); } - for (int i = 0; i < _stringBuilder.Length; i++) + + public int CompareTo(IndentingBuilder? other) { - var cCmp = _stringBuilder[i].CompareTo(other._stringBuilder[i]); - if (cCmp != 0) + if (other is null) + return 1; + + var lenCmp = _stringBuilder.Length.CompareTo(other._stringBuilder.Length); + if (lenCmp != 0) { - return cCmp; + return lenCmp; } + for (int i = 0; i < _stringBuilder.Length; i++) + { + var cCmp = _stringBuilder[i].CompareTo(other._stringBuilder[i]); + if (cCmp != 0) + { + return cCmp; + } + } + return 0; } - return 0; - } - - public void Indent() - { - _currentIndentWhitespace += " "; - } - - public void Dedent() - { - _currentIndentWhitespace = _currentIndentWhitespace[..^4]; - } - - public bool Equals(IndentingBuilder? other) - { - return _stringBuilder.Equals(other?._stringBuilder); - } - public void AppendLine(IndentingBuilder deserialize) - { - Append(deserialize); - _stringBuilder.AppendLine(); - } + public void Indent() + { + _currentIndentWhitespace += " "; + } - [InterpolatedStringHandler] - public ref struct SourceBuilderStringHandler - { - internal readonly StringBuilder _stringBuilder; - private readonly string _originalIndentWhitespace; - private string _currentIndentWhitespace; - private bool _isFirst = true; + public void Dedent() + { + _currentIndentWhitespace = _currentIndentWhitespace.Substring( + 0, + _currentIndentWhitespace.Length - 4 + ); + } - public SourceBuilderStringHandler(int literalLength, int formattedCount) + public bool Equals(IndentingBuilder? other) { - _stringBuilder = new StringBuilder(literalLength); - _originalIndentWhitespace = ""; - _currentIndentWhitespace = ""; + return _stringBuilder.Equals(other?._stringBuilder); } - public SourceBuilderStringHandler( - int literalLength, - int formattedCount, - IndentingBuilder sourceBuilder) + public void AppendLine(IndentingBuilder deserialize) { - _stringBuilder = sourceBuilder._stringBuilder; - _originalIndentWhitespace = sourceBuilder._currentIndentWhitespace; - _currentIndentWhitespace = sourceBuilder._currentIndentWhitespace; + Append(deserialize); + _stringBuilder.AppendLine(); } - public void AppendLiteral(string s) + [InterpolatedStringHandler] + public ref struct SourceBuilderStringHandler { - if (_isFirst) + internal readonly StringBuilder _stringBuilder; + private readonly string _originalIndentWhitespace; + private string _currentIndentWhitespace; + private bool _isFirst = true; + + public SourceBuilderStringHandler(int literalLength, int formattedCount) { - _stringBuilder.Append(_currentIndentWhitespace); - _isFirst = false; + _stringBuilder = new StringBuilder(literalLength); + _originalIndentWhitespace = ""; + _currentIndentWhitespace = ""; } - Append(_stringBuilder, _currentIndentWhitespace, s); - int last = s.LastIndexOf('\n'); - if (last == -1) + public SourceBuilderStringHandler( + int literalLength, + int formattedCount, + IndentingBuilder sourceBuilder + ) { - return; + _stringBuilder = sourceBuilder._stringBuilder; + _originalIndentWhitespace = sourceBuilder._currentIndentWhitespace; + _currentIndentWhitespace = sourceBuilder._currentIndentWhitespace; } - var remaining = s.AsSpan(last + 1); - foreach (var c in remaining) + public void AppendLiteral(string s) { - if (c is not (' ' or '\t')) + if (_isFirst) + { + _stringBuilder.Append(_currentIndentWhitespace); + _isFirst = false; + } + Append(_stringBuilder, _currentIndentWhitespace, s); + + int last = s.LastIndexOf('\n'); + if (last == -1) { return; } - } - _currentIndentWhitespace += remaining.ToString(); - } + var remaining = s.Substring(last + 1); + foreach (var c in remaining) + { + if (c != ' ' && c != '\t') + { + return; + } + } - public void AppendFormatted(T value) - { - if (_isFirst) - { - _stringBuilder.Append(_currentIndentWhitespace); - _isFirst = false; + _currentIndentWhitespace += remaining; } - var str = value?.ToString(); - if (str is null) + + public void AppendFormatted(T value) { - _stringBuilder.Append(str); - return; - } + if (_isFirst) + { + _stringBuilder.Append(_currentIndentWhitespace); + _isFirst = false; + } + var str = value?.ToString(); + if (str is null) + { + _stringBuilder.Append(str); + return; + } - Append(_stringBuilder, _currentIndentWhitespace, str); - _currentIndentWhitespace = _originalIndentWhitespace; + Append(_stringBuilder, _currentIndentWhitespace, str); + _currentIndentWhitespace = _originalIndentWhitespace; + } } } -} \ No newline at end of file +} diff --git a/src/IndentingBuilder/IndentingBuilder.csproj b/src/IndentingBuilder/IndentingBuilder.csproj index 696255d..dcbbc8f 100644 --- a/src/IndentingBuilder/IndentingBuilder.csproj +++ b/src/IndentingBuilder/IndentingBuilder.csproj @@ -1,5 +1,4 @@  - net8.0;netstandard2.0 enable @@ -9,14 +8,14 @@ StaticCS.IndentingBuilder - 0.2.0 + 0.3.0 true MIT https://github.com/agocke/static-cs A string builder with automatic indentation support for multi-line text generation. Distributed as source code for easy inclusion in source generators. agocke string-builder;indentation;code-generation;text-generation;source-only - + false true contentFiles @@ -30,13 +29,4 @@ false - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - diff --git a/src/Result/Result.cs b/src/Result/Result.cs index dfd9706..bc4b549 100644 --- a/src/Result/Result.cs +++ b/src/Result/Result.cs @@ -8,14 +8,17 @@ public abstract record Result private Result() { } public sealed record Ok(TOk Value) : Result; + public sealed record Err(TErr Value) : Result; public static implicit operator Result(TOk success) => new Ok(success); + public static implicit operator Result(TErr error) => new Err(error); - public TOk Unwrap() => this switch - { - Ok(var ok) => ok, - _ => throw new InvalidOperationException(), - }; -} \ No newline at end of file + public TOk Unwrap() => + this switch + { + Ok(var ok) => ok, + _ => throw new InvalidOperationException(), + }; +} diff --git a/src/Result/StaticCs.Result.csproj b/src/Result/StaticCs.Result.csproj index 68abb4d..99eb0ca 100644 --- a/src/Result/StaticCs.Result.csproj +++ b/src/Result/StaticCs.Result.csproj @@ -1,5 +1,4 @@  - net8.0 0.1.0 @@ -17,5 +16,4 @@ - diff --git a/src/StaticCs.ContentFiles/ClosedAttribute.cs b/src/StaticCs.ContentFiles/ClosedAttribute.cs index f151ab8..1751b4e 100644 --- a/src/StaticCs.ContentFiles/ClosedAttribute.cs +++ b/src/StaticCs.ContentFiles/ClosedAttribute.cs @@ -1,8 +1,7 @@ - using System; namespace StaticCs { [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Class)] internal sealed class ClosedAttribute : Attribute { } -} \ No newline at end of file +} diff --git a/src/StaticCs.TrimmableConverter/StaticCs.TrimmableConverter.csproj b/src/StaticCs.TrimmableConverter/StaticCs.TrimmableConverter.csproj index 392f52e..1cbe7ac 100644 --- a/src/StaticCs.TrimmableConverter/StaticCs.TrimmableConverter.csproj +++ b/src/StaticCs.TrimmableConverter/StaticCs.TrimmableConverter.csproj @@ -1,5 +1,4 @@  - netstandard2.0 enable @@ -28,5 +27,4 @@ all - diff --git a/src/StaticCs.TrimmableConverter/TrimmableTypeConverter.cs b/src/StaticCs.TrimmableConverter/TrimmableTypeConverter.cs index 37e1d7c..9a8f699 100644 --- a/src/StaticCs.TrimmableConverter/TrimmableTypeConverter.cs +++ b/src/StaticCs.TrimmableConverter/TrimmableTypeConverter.cs @@ -12,7 +12,7 @@ public static class TrimmableTypeConverter private static readonly Dictionary _intrinsicConverters = new() { [typeof(bool)] = new BooleanConverter(), - [typeof(byte)] = new ByteConverter(), + [typeof(byte)] = new ByteConverter(), [typeof(sbyte)] = new SByteConverter(), [typeof(char)] = new CharConverter(), [typeof(double)] = new DoubleConverter(), @@ -34,7 +34,9 @@ public static class TrimmableTypeConverter [typeof(Uri)] = new UriTypeConverter(), }; - internal const DynamicallyAccessedMemberTypes ConverterAnnotation = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields; + internal const DynamicallyAccessedMemberTypes ConverterAnnotation = + DynamicallyAccessedMemberTypes.PublicParameterlessConstructor + | DynamicallyAccessedMemberTypes.PublicFields; public static TypeConverter GetConverter([DAM(ConverterAnnotation)] Type type) { @@ -57,7 +59,8 @@ public static TypeConverter GetConverter([DAM(ConverterAnnotation)] Type type) return new TypeConverter(); } - public static TypeConverter GetConverter<[DAM(ConverterAnnotation)] T>() => GetConverter(typeof(T)); + public static TypeConverter GetConverter<[DAM(ConverterAnnotation)] T>() => + GetConverter(typeof(T)); private static readonly ArrayConverter s_arrayConverter = new(); private static readonly CollectionConverter s_collectionConverter = new(); @@ -90,4 +93,3 @@ public static TypeConverter GetConverter([DAM(ConverterAnnotation)] Type type) return null; } } - diff --git a/src/StaticCs/ClosedDeclarationChecker.cs b/src/StaticCs/ClosedDeclarationChecker.cs index 0740e3c..e3789bf 100644 --- a/src/StaticCs/ClosedDeclarationChecker.cs +++ b/src/StaticCs/ClosedDeclarationChecker.cs @@ -1,4 +1,3 @@ - using System.Collections.Immutable; using System.Runtime.InteropServices.ComTypes; using System.Security.Claims; @@ -13,37 +12,44 @@ namespace StaticCs; public sealed class ClosedDeclarationChecker : DiagnosticAnalyzer { private static readonly DiagnosticDescriptor s_descriptor = new( - id: DiagId.ClassOrRecordMustBeClosed.ToIdString(), - title: "[Closed] is only valid on classes/records with closed hierarchies", - messageFormat: "[Closed] is only valid on abstract classes/records with private constructors", - category: "StaticCs", - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true); + id: DiagId.ClassOrRecordMustBeClosed.ToIdString(), + title: "[Closed] is only valid on classes/records with closed hierarchies", + messageFormat: "[Closed] is only valid on abstract classes/records with private constructors", + category: "StaticCs", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true + ); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(s_descriptor); + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create(s_descriptor); public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.RegisterSyntaxNodeAction(ctx => - { - var node = (AttributeListSyntax)ctx.Node; - if (node.Parent is not TypeDeclarationSyntax typeDecl) - return; - SemanticModel? model = null; - foreach (var attr in node.Attributes) + context.RegisterSyntaxNodeAction( + ctx => { - if (!attr.Name.ToFullString().Contains("Closed")) - continue; - model ??= ctx.SemanticModel; - if (IsClosedAttribute(model.GetTypeInfo(attr).Type) && - !IsTypeClosed(model.GetDeclaredSymbol(typeDecl))) + var node = (AttributeListSyntax)ctx.Node; + if (node.Parent is not TypeDeclarationSyntax typeDecl) + return; + SemanticModel? model = null; + foreach (var attr in node.Attributes) { - ctx.ReportDiagnostic(Diagnostic.Create(s_descriptor, attr.GetLocation())); + if (!attr.Name.ToFullString().Contains("Closed")) + continue; + model ??= ctx.SemanticModel; + if ( + IsClosedAttribute(model.GetTypeInfo(attr).Type) + && !IsTypeClosed(model.GetDeclaredSymbol(typeDecl)) + ) + { + ctx.ReportDiagnostic(Diagnostic.Create(s_descriptor, attr.GetLocation())); + } } - } - }, SyntaxKind.AttributeList); + }, + SyntaxKind.AttributeList + ); // A type is considered "closed" if it is an enum or it is an abstract class or record with no non-private constructors. static bool IsTypeClosed(INamedTypeSymbol? type) @@ -65,7 +71,10 @@ static bool IsTypeClosed(INamedTypeSymbol? type) foreach (var ctor in type.InstanceConstructors) { // Skip copy constructor - if (ctor.Parameters.Length == 1 && ctor.Parameters[0].Type.Equals(type, SymbolEqualityComparer.Default)) + if ( + ctor.Parameters.Length == 1 + && ctor.Parameters[0].Type.Equals(type, SymbolEqualityComparer.Default) + ) { continue; } @@ -81,14 +90,11 @@ static bool IsTypeClosed(INamedTypeSymbol? type) public static bool IsClosedAttribute(ITypeSymbol? type) { - return type is { - Name: "ClosedAttribute", - ContainingNamespace: { - Name: "StaticCs", - ContainingNamespace: { - IsGlobalNamespace: true - } - } - }; + return type + is { + Name: "ClosedAttribute", + ContainingNamespace: + { Name: "StaticCs", ContainingNamespace: { IsGlobalNamespace: true } } + }; } -} \ No newline at end of file +} diff --git a/src/StaticCs/ClosedTypeCompletenessSuppressor.cs b/src/StaticCs/ClosedTypeCompletenessSuppressor.cs index d3d0d00..7732b27 100644 --- a/src/StaticCs/ClosedTypeCompletenessSuppressor.cs +++ b/src/StaticCs/ClosedTypeCompletenessSuppressor.cs @@ -14,16 +14,18 @@ namespace StaticCs; [DiagnosticAnalyzer("C#")] public class ClosedTypeCompletenessSuppressor : DiagnosticSuppressor { - private readonly static SuppressionDescriptor s_enumDescriptor = new SuppressionDescriptor( + private static readonly SuppressionDescriptor s_enumDescriptor = new SuppressionDescriptor( DiagId.SwitchOnClosedSuppress.ToIdString(), "CS8524", - "Enum is marked [Closed]"); - private readonly static SuppressionDescriptor s_classDescriptor = new SuppressionDescriptor( + "Enum is marked [Closed]" + ); + private static readonly SuppressionDescriptor s_classDescriptor = new SuppressionDescriptor( DiagId.SwitchOnClosedSuppress.ToIdString(), "CS8509", - "Enum is marked [Closed]"); - public override ImmutableArray SupportedSuppressions { get; } = ImmutableArray.Create(s_enumDescriptor, s_classDescriptor); - + "Enum is marked [Closed]" + ); + public override ImmutableArray SupportedSuppressions { get; } = + ImmutableArray.Create(s_enumDescriptor, s_classDescriptor); public override void ReportSuppressions(SuppressionAnalysisContext context) { @@ -71,7 +73,9 @@ public override void ReportSuppressions(SuppressionAnalysisContext context) subTypes.Remove(namedType); } } - else if (arm.Pattern is DeclarationPatternSyntax { Type: { } patternSyntax }) + else if ( + arm.Pattern is DeclarationPatternSyntax { Type: { } patternSyntax } + ) { var symbolInfo = model.GetSymbolInfo(patternSyntax); if (symbolInfo.Symbol is INamedTypeSymbol namedType) @@ -79,15 +83,23 @@ public override void ReportSuppressions(SuppressionAnalysisContext context) subTypes.Remove(namedType); } } - else if (arm.Pattern is RecursivePatternSyntax - { - Type: { } patternTypeSyntax, - PositionalPatternClause: PositionalPatternClauseSyntax { Subpatterns: { } subpatterns } - }) + else if ( + arm.Pattern is RecursivePatternSyntax + { + Type: { } patternTypeSyntax, + PositionalPatternClause: PositionalPatternClauseSyntax + { + Subpatterns: { } subpatterns + } + } + ) { - if (model.GetSymbolInfo(patternTypeSyntax).Symbol is INamedTypeSymbol patternType && - subTypes.Contains(patternType) && - IsIrrefutablePositional(subpatterns, patternType, model)) + if ( + model.GetSymbolInfo(patternTypeSyntax).Symbol + is INamedTypeSymbol patternType + && subTypes.Contains(patternType) + && IsIrrefutablePositional(subpatterns, patternType, model) + ) { subTypes.Remove(patternType); } @@ -99,7 +111,6 @@ public override void ReportSuppressions(SuppressionAnalysisContext context) context.ReportSuppression(Suppression.Create(s_classDescriptor, diag)); break; } - } } } @@ -109,19 +120,28 @@ public override void ReportSuppressions(SuppressionAnalysisContext context) private static bool IsIrrefutablePositional( SeparatedSyntaxList subpatterns, INamedTypeSymbol patternType, - SemanticModel model) + SemanticModel model + ) { var matchingDeconstructors = patternType .GetMembers() .OfType() - .Where(m => m is { Name: "Deconstruct", Parameters: { Length: var paramCount } } && - paramCount == subpatterns.Count); + .Where(m => + m is { Name: "Deconstruct", Parameters: { Length: var paramCount } } + && paramCount == subpatterns.Count + ); foreach (var deconstruct in matchingDeconstructors) { bool matched = true; for (int i = 0; i < deconstruct.Parameters.Length; i++) { - if (!IsSubpatternIrrefutable(subpatterns[i].Pattern, deconstruct.Parameters[i].Type, model)) + if ( + !IsSubpatternIrrefutable( + subpatterns[i].Pattern, + deconstruct.Parameters[i].Type, + model + ) + ) { matched = false; break; @@ -137,7 +157,11 @@ private static bool IsIrrefutablePositional( // Check for an irrefutable match of a positional subpattern against a // Deconstruct parameter - private static bool IsSubpatternIrrefutable(PatternSyntax pattern, ITypeSymbol paramType, SemanticModel model) + private static bool IsSubpatternIrrefutable( + PatternSyntax pattern, + ITypeSymbol paramType, + SemanticModel model + ) { switch (pattern) { diff --git a/src/StaticCs/DiagId.cs b/src/StaticCs/DiagId.cs index a0772d8..db16c84 100644 --- a/src/StaticCs/DiagId.cs +++ b/src/StaticCs/DiagId.cs @@ -1,4 +1,3 @@ - namespace StaticCs; public enum DiagId @@ -13,4 +12,4 @@ public static class DiagUtils private const string DiagPrefix = "STATICCS"; public static string ToIdString(this DiagId id) => $"{DiagPrefix}{(int)id:D3}"; -} \ No newline at end of file +} diff --git a/src/StaticCs/EnumClosedConversionAnalyzer.cs b/src/StaticCs/EnumClosedConversionAnalyzer.cs index 5edda05..e0520b0 100644 --- a/src/StaticCs/EnumClosedConversionAnalyzer.cs +++ b/src/StaticCs/EnumClosedConversionAnalyzer.cs @@ -1,4 +1,3 @@ - using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -11,35 +10,42 @@ namespace StaticCs; public sealed class EnumClosedConversionAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor s_descriptor = new DiagnosticDescriptor( - id: DiagId.ClosedEnumConversion.ToIdString(), - title: "Integers cannot be converted to [Closed] enums", - messageFormat: "Integer conversions to [Closed] enum {0} are disallowed", - category: "StaticCs", - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true); + id: DiagId.ClosedEnumConversion.ToIdString(), + title: "Integers cannot be converted to [Closed] enums", + messageFormat: "Integer conversions to [Closed] enum {0} are disallowed", + category: "StaticCs", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true + ); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(s_descriptor); + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create(s_descriptor); public override void Initialize(AnalysisContext ctx) { ctx.EnableConcurrentExecution(); ctx.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics); - ctx.RegisterSyntaxNodeAction(ctx => - { - var castSyntax = (CastExpressionSyntax)ctx.Node; - var model = ctx.SemanticModel; - var targetTypeInfo = model.GetTypeInfo(castSyntax.Type); - - if (targetTypeInfo.Type is { TypeKind: TypeKind.Enum } type) + ctx.RegisterSyntaxNodeAction( + ctx => { - foreach (var attr in type.GetAttributes()) + var castSyntax = (CastExpressionSyntax)ctx.Node; + var model = ctx.SemanticModel; + var targetTypeInfo = model.GetTypeInfo(castSyntax.Type); + + if (targetTypeInfo.Type is { TypeKind: TypeKind.Enum } type) { - if (attr.AttributeClass?.ToDisplayString() == "StaticCs.ClosedAttribute") + foreach (var attr in type.GetAttributes()) { - ctx.ReportDiagnostic(Diagnostic.Create(s_descriptor, castSyntax.GetLocation(), type)); + if (attr.AttributeClass?.ToDisplayString() == "StaticCs.ClosedAttribute") + { + ctx.ReportDiagnostic( + Diagnostic.Create(s_descriptor, castSyntax.GetLocation(), type) + ); + } } } - } - }, SyntaxKind.CastExpression); + }, + SyntaxKind.CastExpression + ); } -} \ No newline at end of file +} diff --git a/src/StaticCs/StaticCs.csproj b/src/StaticCs/StaticCs.csproj index fce2816..21fbb8f 100644 --- a/src/StaticCs/StaticCs.csproj +++ b/src/StaticCs/StaticCs.csproj @@ -1,5 +1,4 @@ - netstandard2.0 enable @@ -22,12 +21,15 @@ - + true contentFiles/cs/any compile - diff --git a/test/AotTest/AotTest.csproj b/test/AotTest/AotTest.csproj index ecca2e9..684e473 100644 --- a/test/AotTest/AotTest.csproj +++ b/test/AotTest/AotTest.csproj @@ -1,5 +1,4 @@  - Exe net8.0 @@ -18,5 +17,4 @@ - diff --git a/test/AotTest/Program.cs b/test/AotTest/Program.cs index 22aba51..70e2c6e 100644 --- a/test/AotTest/Program.cs +++ b/test/AotTest/Program.cs @@ -1,7 +1,7 @@ // See https://aka.ms/new-console-template for more information -using StaticCs; using System.ComponentModel; +using StaticCs; using Xunit; // Primitive types @@ -21,11 +21,9 @@ Assert.IsType(TrimmableTypeConverter.GetConverter(typeof(Converted))); Assert.IsType(TrimmableTypeConverter.GetConverter(typeof(Unconverted))); -class Unconverted {} +class Unconverted { } [TypeConverter(typeof(MyConverter))] -class Converted -{ -} +class Converted { } -class MyConverter : TypeConverter { } \ No newline at end of file +class MyConverter : TypeConverter { } diff --git a/test/test/AsyncTests.cs b/test/test/AsyncTests.cs index fb0324e..2d1b714 100644 --- a/test/test/AsyncTests.cs +++ b/test/test/AsyncTests.cs @@ -1,4 +1,3 @@ - using System; using System.Threading; using System.Threading.Tasks; @@ -10,6 +9,7 @@ namespace StaticCs.Async.Tests; public sealed class AsyncTests { private ITestOutputHelper _output; + public AsyncTests(ITestOutputHelper output) { _output = output; @@ -19,12 +19,14 @@ public AsyncTests(ITestOutputHelper output) public async Task ExceptionInBackgroundCancelsForeground() { CancellationToken token = default; - await Assert.ThrowsAsync(() => TaskScope.With(async scope => - { - token = scope.CancellationToken; - var task = scope.Run(() => throw new InvalidOperationException("Test")); - await Task.Delay(TimeSpan.FromSeconds(1), token); - })); + await Assert.ThrowsAsync(() => + TaskScope.With(async scope => + { + token = scope.CancellationToken; + var task = scope.Run(() => throw new InvalidOperationException("Test")); + await Task.Delay(TimeSpan.FromSeconds(1), token); + }) + ); Assert.True(token.IsCancellationRequested); } @@ -33,16 +35,18 @@ public async Task ExceptionInForegroundCancelsBackground() { CancellationToken token = default; Task? backgroundTask = null; - await Assert.ThrowsAsync(() => TaskScope.With(scope => - { - token = scope.CancellationToken; - backgroundTask = scope.Run(async () => + await Assert.ThrowsAsync(() => + TaskScope.With(scope => { - await Task.Delay(TimeSpan.FromSeconds(10), token); - _output.WriteLine("Background task completed."); - }); - throw new InvalidOperationException("Test"); - })); + token = scope.CancellationToken; + backgroundTask = scope.Run(async () => + { + await Task.Delay(TimeSpan.FromSeconds(10), token); + _output.WriteLine("Background task completed."); + }); + throw new InvalidOperationException("Test"); + }) + ); Assert.True(token.IsCancellationRequested); Assert.NotNull(backgroundTask); Assert.Equal(TaskStatus.Canceled, backgroundTask.Status); @@ -101,4 +105,4 @@ public Task ExceptionRethrownIfNoBackgroundCanceled() await scope; }); } -} \ No newline at end of file +} diff --git a/test/test/ClosedTests.cs b/test/test/ClosedTests.cs index bd03399..529c5aa 100644 --- a/test/test/ClosedTests.cs +++ b/test/test/ClosedTests.cs @@ -30,9 +30,14 @@ class C }; } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(5,27): warning CS8524: The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(Rgb)3' is not covered. - DiagnosticResult.CompilerWarning("CS8524").WithSpan(5, 27, 5, 33).WithArguments("(Rgb)3")); + DiagnosticResult + .CompilerWarning("CS8524") + .WithSpan(5, 27, 5, 33) + .WithArguments("(Rgb)3") + ); } [Fact] @@ -52,9 +57,15 @@ class C }; } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(6,27): warning CS8524: The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(Rgb)3' is not covered. - DiagnosticResult.CompilerWarning("CS8524").WithSpan(6, 27, 6, 33).WithArguments("(Rgb)3").WithIsSuppressed(true)); + DiagnosticResult + .CompilerWarning("CS8524") + .WithSpan(6, 27, 6, 33) + .WithArguments("(Rgb)3") + .WithIsSuppressed(true) + ); } [Fact] @@ -74,9 +85,11 @@ void M() } } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(9,19): error STATICCS002: Integer conversions to [Closed] enum Rgb are disallowed - ClosedEnumConversion.WithSpan(9, 19, 9, 26).WithArguments("Rgb")); + ClosedEnumConversion.WithSpan(9, 19, 9, 26).WithArguments("Rgb") + ); } [Fact] @@ -107,9 +120,11 @@ public async Task ClosedOnNonAbstractClass() class Base { } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(1,2): error STATICCS003: [Closed] is only valid on abstract classes/records with private constructors - ClassOrRecordMustBeClosed.WithSpan(1, 2, 1, 17)); + ClassOrRecordMustBeClosed.WithSpan(1, 2, 1, 17) + ); } [Fact] @@ -119,9 +134,11 @@ public async Task ClosedOnAbstractClassWithPublicConstructor() [StaticCs.Closed] abstract class Base { } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(1,2): error STATICCS003: [Closed] is only valid on abstract classes/records with private constructors - ClassOrRecordMustBeClosed.WithSpan(1, 2, 1, 17)); + ClassOrRecordMustBeClosed.WithSpan(1, 2, 1, 17) + ); } [Fact] @@ -168,9 +185,11 @@ class C }; } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(8,24): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. - DiagnosticResult.CompilerWarning("CS8509").WithSpan(8, 24, 8, 30).WithArguments("_")); + DiagnosticResult.CompilerWarning("CS8509").WithSpan(8, 24, 8, 30).WithArguments("_") + ); } [Fact] @@ -193,9 +212,15 @@ class C }; } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(10,24): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. - DiagnosticResult.CompilerWarning("CS8509").WithSpan(10, 24, 10, 30).WithArguments("_").WithIsSuppressed(true)); + DiagnosticResult + .CompilerWarning("CS8509") + .WithSpan(10, 24, 10, 30) + .WithArguments("_") + .WithIsSuppressed(true) + ); } [Fact] @@ -217,9 +242,11 @@ class C }; } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(10,24): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. - DiagnosticResult.CompilerWarning("CS8509").WithSpan(10, 24, 10, 30).WithArguments("_")); + DiagnosticResult.CompilerWarning("CS8509").WithSpan(10, 24, 10, 30).WithArguments("_") + ); } [Fact] @@ -242,9 +269,15 @@ class C }; } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(10,31): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. - DiagnosticResult.CompilerWarning("CS8509").WithSpan(10, 31, 10, 37).WithArguments("_").WithIsSuppressed(true)); + DiagnosticResult + .CompilerWarning("CS8509") + .WithSpan(10, 31, 10, 37) + .WithArguments("_") + .WithIsSuppressed(true) + ); } [Fact] @@ -267,9 +300,14 @@ class C }; } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(10,31): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Option.Some(0) { }' is not covered. - DiagnosticResult.CompilerWarning("CS8509").WithSpan(10, 31, 10, 37).WithArguments("Option.Some(0) { }")); + DiagnosticResult + .CompilerWarning("CS8509") + .WithSpan(10, 31, 10, 37) + .WithArguments("Option.Some(0) { }") + ); } [Fact] @@ -292,30 +330,47 @@ class C }; } """; - await VerifyDiagnostics(src, + await VerifyDiagnostics( + src, // /0/Test0.cs(10,24): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. - DiagnosticResult.CompilerWarning("CS8509").WithSpan(10, 24, 10, 30).WithArguments("_").WithIsSuppressed(true)); + DiagnosticResult + .CompilerWarning("CS8509") + .WithSpan(10, 24, 10, 30) + .WithArguments("_") + .WithIsSuppressed(true) + ); } - private static readonly DiagnosticResult ClosedEnumConversion = CSharpAnalyzerVerifier.Diagnostic(DiagId.ClosedEnumConversion.ToIdString()); - private static readonly DiagnosticResult ClassOrRecordMustBeClosed = CSharpAnalyzerVerifier.Diagnostic(DiagId.ClassOrRecordMustBeClosed.ToIdString()); + private static readonly DiagnosticResult ClosedEnumConversion = CSharpAnalyzerVerifier< + EnumClosedConversionAnalyzer, + XUnitVerifier + >.Diagnostic(DiagId.ClosedEnumConversion.ToIdString()); + private static readonly DiagnosticResult ClassOrRecordMustBeClosed = CSharpAnalyzerVerifier< + ClosedDeclarationChecker, + XUnitVerifier + >.Diagnostic(DiagId.ClassOrRecordMustBeClosed.ToIdString()); - private Task VerifyDiagnostics(string src, params DiagnosticResult[] expected) where TAnalyzer : DiagnosticAnalyzer, new() + private Task VerifyDiagnostics(string src, params DiagnosticResult[] expected) + where TAnalyzer : DiagnosticAnalyzer, new() { var test = new SuppressorTest { TestCode = src, CompilerDiagnostics = CompilerDiagnostics.Warnings, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, - TestBehaviors = TestBehaviors.SkipSuppressionCheck + TestBehaviors = TestBehaviors.SkipSuppressionCheck, }; test.TestState.Sources.Add( - File.ReadAllText(Path.Combine(CurrentPath(), "../../../src/StaticCs.ContentFiles/ClosedAttribute.cs"))); + File.ReadAllText( + Path.Combine(CurrentPath(), "../../../src/StaticCs.ContentFiles/ClosedAttribute.cs") + ) + ); test.ExpectedDiagnostics.AddRange(expected); return test.RunAsync(); } - private static string CurrentPath([CallerFilePath] string? path = null) => path ?? throw new InvalidOperationException(); + private static string CurrentPath([CallerFilePath] string? path = null) => + path ?? throw new InvalidOperationException(); } //using System; //using StaticCs; @@ -364,4 +419,4 @@ await VerifyDiagnostics(src, // Green, // Blue //} -// \ No newline at end of file +// diff --git a/test/test/CollectionsTests.cs b/test/test/CollectionsTests.cs index 4cdac44..28eb11e 100644 --- a/test/test/CollectionsTests.cs +++ b/test/test/CollectionsTests.cs @@ -1,4 +1,3 @@ - using System.Collections.Generic; using StaticCs.Collections; using Xunit; @@ -9,7 +8,9 @@ public class CollectionsTests { private enum RGB { - Red, Green, Blue + Red, + Green, + Blue, } [Fact] diff --git a/test/test/IndentingBuilderTests.cs b/test/test/IndentingBuilderTests.cs index 2e83fba..c0d9da6 100644 --- a/test/test/IndentingBuilderTests.cs +++ b/test/test/IndentingBuilderTests.cs @@ -91,9 +91,13 @@ public void Indent_IncreasesIndentation() builder.Indent(); builder.AppendLine("Level 2"); - var expected = "Level 0" + Environment.NewLine + - " Level 1" + Environment.NewLine + - " Level 2" + Environment.NewLine; + var expected = + "Level 0" + + Environment.NewLine + + " Level 1" + + Environment.NewLine + + " Level 2" + + Environment.NewLine; Assert.Equal(expected, builder.ToString()); } @@ -107,9 +111,13 @@ public void Dedent_DecreasesIndentation() builder.Dedent(); builder.AppendLine("Back to Level 0"); - var expected = "Level 0" + Environment.NewLine + - " Level 1" + Environment.NewLine + - "Back to Level 0" + Environment.NewLine; + var expected = + "Level 0" + + Environment.NewLine + + " Level 1" + + Environment.NewLine + + "Back to Level 0" + + Environment.NewLine; Assert.Equal(expected, builder.ToString()); } @@ -130,12 +138,19 @@ public void Indent_Dedent_MultipleLevel_WorksCorrectly() builder.Dedent(); builder.AppendLine("Level 0"); - var expected = "Level 0" + Environment.NewLine + - " Level 1" + Environment.NewLine + - " Level 2" + Environment.NewLine + - " Level 3" + Environment.NewLine + - " Level 1" + Environment.NewLine + - "Level 0" + Environment.NewLine; + var expected = + "Level 0" + + Environment.NewLine + + " Level 1" + + Environment.NewLine + + " Level 2" + + Environment.NewLine + + " Level 3" + + Environment.NewLine + + " Level 1" + + Environment.NewLine + + "Level 0" + + Environment.NewLine; Assert.Equal(expected, builder.ToString()); } @@ -146,9 +161,8 @@ public void Append_StringWithNewlines_PreservesIndentation() builder.Indent(); builder.Append("Line 1\nLine 2\nLine 3"); - var expected = " Line 1" + Environment.NewLine + - " Line 2" + Environment.NewLine + - " Line 3"; + var expected = + " Line 1" + Environment.NewLine + " Line 2" + Environment.NewLine + " Line 3"; Assert.Equal(expected, builder.ToString()); } @@ -260,9 +274,7 @@ public void AppendLine_WithIndentingBuilder_PreservesIndentation() builder2.AppendLine(builder1); builder2.Append("After"); - var expected = " FirstSecond" + Environment.NewLine + - Environment.NewLine + - " After"; + var expected = " FirstSecond" + Environment.NewLine + Environment.NewLine + " After"; Assert.Equal(expected, builder2.ToString()); } @@ -309,10 +321,14 @@ public void InterpolatedString_WithMultilineContent_PreservesIndentation() builder.Append($"Start\n{multiline}\nEnd"); // After a formatted value, indentation resets to the original level - var expected = " Start" + Environment.NewLine + - "Line1" + Environment.NewLine + - " Line2" + Environment.NewLine + - " End"; + var expected = + " Start" + + Environment.NewLine + + "Line1" + + Environment.NewLine + + " Line2" + + Environment.NewLine + + " End"; Assert.Equal(expected, builder.ToString()); } @@ -340,14 +356,23 @@ public void ComplexScenario_MixedIndentationAndContent_WorksCorrectly() builder.Dedent(); builder.AppendLine("}"); - var expected = "public class Example" + Environment.NewLine + - "{" + Environment.NewLine + - " public void Method()" + Environment.NewLine + - " {" + Environment.NewLine + - " Console.WriteLine(\"Hello\");" + Environment.NewLine + - " Console.WriteLine(\"World\");" + Environment.NewLine + - " }" + Environment.NewLine + - "}" + Environment.NewLine; + var expected = + "public class Example" + + Environment.NewLine + + "{" + + Environment.NewLine + + " public void Method()" + + Environment.NewLine + + " {" + + Environment.NewLine + + " Console.WriteLine(\"Hello\");" + + Environment.NewLine + + " Console.WriteLine(\"World\");" + + Environment.NewLine + + " }" + + Environment.NewLine + + "}" + + Environment.NewLine; Assert.Equal(expected, builder.ToString()); } diff --git a/test/test/SuppressorTest.cs b/test/test/SuppressorTest.cs index a3c263a..28576e3 100644 --- a/test/test/SuppressorTest.cs +++ b/test/test/SuppressorTest.cs @@ -1,4 +1,3 @@ - using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis; @@ -13,9 +12,11 @@ namespace StaticCs.Tests; internal class SuppressorTest : CSharpAnalyzerTest where TAnalyzer : DiagnosticAnalyzer, new() { - public CSharpCompilationOptions CompilationOptions { get; private init; } - = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true) - .WithReportSuppressedDiagnostics(true); + public CSharpCompilationOptions CompilationOptions { get; private init; } = + new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary, + allowUnsafe: true + ).WithReportSuppressedDiagnostics(true); public SuppressorTest() { } @@ -26,11 +27,26 @@ private SuppressorTest(SuppressorTest other) protected override CompilationOptions CreateCompilationOptions() => CompilationOptions; - protected override CompilationWithAnalyzers CreateCompilationWithAnalyzers(Compilation compilation, ImmutableArray analyzers, AnalyzerOptions options, CancellationToken cancellationToken) - => new CompilationWithAnalyzers(compilation, analyzers, new CompilationWithAnalyzersOptions(options, onAnalyzerException: null, concurrentAnalysis: false, logAnalyzerExecutionTime: false, reportSuppressedDiagnostics: true)); + protected override CompilationWithAnalyzers CreateCompilationWithAnalyzers( + Compilation compilation, + ImmutableArray analyzers, + AnalyzerOptions options, + CancellationToken cancellationToken + ) => + new CompilationWithAnalyzers( + compilation, + analyzers, + new CompilationWithAnalyzersOptions( + options, + onAnalyzerException: null, + concurrentAnalysis: false, + logAnalyzerExecutionTime: false, + reportSuppressedDiagnostics: true + ) + ); public SuppressorTest WithCompilationOptions(CSharpCompilationOptions options) { return new(this) { CompilationOptions = options }; } -} \ No newline at end of file +} diff --git a/test/test/test.csproj b/test/test/test.csproj index 9aefe46..eff403d 100644 --- a/test/test/test.csproj +++ b/test/test/test.csproj @@ -1,5 +1,4 @@ - net8.0 enable @@ -7,11 +6,14 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all + + runtime; build; native; contentfiles; analyzers; buildtransitive + all - + @@ -25,7 +27,9 @@ - + + +