diff --git a/AGENTS.md b/AGENTS.md index 9962475..4ba88a6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -112,9 +112,7 @@ src/Zomp.SyncMethodGenerator/ Generator (netstandard2.0) SourceGenerationHelper.cs Attribute definitions Extensions.cs Type-checking extensions on INamedTypeSymbol DiagnosticMessages.cs ZSMGEN001-006 diagnostic descriptors - Cloning/ Copying a method into a file of its own - Models/ Data records for the pipeline - Helpers/ EquatableArray, DirectiveStack, etc. + Helpers/ CompilationExtensions, LoopExitWalker Properties/ Assembly attributes tools/ MSBuild props/targets shipped in the NuGet package src/Zomp.SyncMethodGenerator.Pack/ Packing-only project (no code) @@ -124,18 +122,27 @@ tests/GenerationSandbox.Tests/ Integration tests (real-world patterns) ## Cloning layer -`Cloning/` holds everything about emitting a copy of a method that has nothing -to do with async: finding the marked methods (`CloneTarget`), collecting the -namespaces, usings and containing types around them (`MethodLocation`), fully -qualifying the names a method uses (`CloningRewriter`), detecting colliding -signatures and naming the files (`ClonedMethodOutput`), and writing the file -(`ClonedMethodSource`). - -Keep async knowledge out of it. A transformation plugs in by deriving from -`CloningRewriter`, overriding the visitors it needs, and using the `MapSymbol` -and `MapTypeName` hooks to substitute types. Nothing under `Cloning/` should -know about tasks or `SYNC_ONLY`; a grep for `Task|Async|SYNC_ONLY` there should -come back empty. +Everything about emitting a copy of a method that has nothing to do with async +comes from the `Zomp.MethodCloning` source package, maintained in the +`zompinc/method-cloning` repository: finding the marked methods +(`CloneTarget`), collecting the namespaces, usings and containing types around +them (`MethodLocation`), fully qualifying the names a method uses +(`CloningRewriter`), detecting colliding signatures and naming the files +(`ClonedMethodOutput`), and writing the file (`ClonedMethodSource`). + +The package ships source, not a DLL. Its files compile into the generator as +internal types in the `Zomp.MethodCloning` namespace, with the `ROSLYN_*` +constants this repository defines, so each Roslyn variant gets its own build of +them. A change to them belongs in that repository: fix it there, publish it, and +bump the version in `Directory.Packages.props`. `AsyncToSyncRewriter` plugs in +by deriving from `CloningRewriter` and using its `MapSymbol` and `MapTypeName` +hooks. + +A cloning bug gets its failing test and its fix in that repository, as a test of +its identity clone, whichever generator it was found through. This repository +adds a test of its own only when the bug shows up through the async to sync +rules. Tests which exercise nothing but cloning - file names, containing types, +name qualification, namespaces - live there, not here. ## Transformation Pipeline @@ -163,7 +170,8 @@ come back empty. - `TestHelper` also clones every synchronized method through `IdentityCloneGenerator`, which changes nothing but the method's name, and fails the test with "Identity clone does not compile" when a clone does not. - That failure is in the `Cloning/` layer, not in the async to sync rules + That failure is in the `Zomp.MethodCloning` package, not in the async to sync + rules, and is fixed there ### Choosing how the source is wrapped @@ -220,7 +228,7 @@ it: check out the test commit alone and watch it fail. - File-scoped namespaces required - Nerdbank.GitVersioning for version management (from git tags/height) - The generator targets `netstandard2.0` for maximum host compatibility -- `EquatableArray` wraps `ImmutableArray` for value equality in the incremental pipeline +- `EquatableArray`, from `Zomp.MethodCloning`, wraps `ImmutableArray` for value equality in the incremental pipeline ## Diagnostics diff --git a/Directory.Packages.props b/Directory.Packages.props index f3875a6..3d2f63a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,5 +17,6 @@ + diff --git a/src/Zomp.SyncMethodGenerator/Cloning/CloneTarget.cs b/src/Zomp.SyncMethodGenerator/Cloning/CloneTarget.cs deleted file mode 100644 index c57662e..0000000 --- a/src/Zomp.SyncMethodGenerator/Cloning/CloneTarget.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace Zomp.SyncMethodGenerator.Cloning; - -/// -/// A method marked for cloning, either by an attribute of its own or by one on its containing type. -/// -/// Context of the attribute which marked the method. -/// Declaration of the method. -internal sealed record CloneTarget(GeneratorAttributeSyntaxContext Context, MethodDeclarationSyntax Syntax) -{ - /// - /// Finds the methods an attribute marks. One on a type marks every method the type declares. - /// - /// Syntax provider of the generator. - /// Name of the attribute. - /// A method for each method marked. - public static IncrementalValuesProvider ForAttribute(SyntaxValueProvider syntaxProvider, string fullyQualifiedMetadataName) - => syntaxProvider - .ForAttributeWithMetadataName( - fullyQualifiedMetadataName, - predicate: static (s, _) => IsCandidate(s), - transform: static (ctx, ct) => Expand(ctx, ct)) - .SelectMany((list, ct) => list); - - private static bool IsCandidate(SyntaxNode node) => node switch - { - MethodDeclarationSyntax { AttributeLists.Count: > 0 } => true, - ClassDeclarationSyntax { AttributeLists.Count: > 0 } => true, - StructDeclarationSyntax { AttributeLists.Count: > 0 } => true, - InterfaceDeclarationSyntax { AttributeLists.Count: > 0 } => true, - RecordDeclarationSyntax { AttributeLists.Count: > 0 } => true, - _ => false, - }; - - private static ImmutableArray Expand(GeneratorAttributeSyntaxContext ctx, CancellationToken ct) - { - ct.ThrowIfCancellationRequested(); - - if (ctx.TargetNode is TypeDeclarationSyntax typeDecl) - { -#if ROSLYN_4_12_OR_GREATER - return [.. typeDecl.Members.OfType().Select(s => new CloneTarget(ctx, s))]; -#else - return ImmutableArray.CreateRange(typeDecl.Members.OfType().Select(s => new CloneTarget(ctx, s))); -#endif - } - else if (ctx.TargetNode is MethodDeclarationSyntax methodDecl) - { -#if ROSLYN_4_12_OR_GREATER - return [new CloneTarget(ctx, methodDecl)]; -#else - return ImmutableArray.Create(new CloneTarget(ctx, methodDecl)); -#endif - } - -#if ROSLYN_4_12_OR_GREATER - return []; -#else - return ImmutableArray.Empty; -#endif - } -} diff --git a/src/Zomp.SyncMethodGenerator/Cloning/ClonedMethod.cs b/src/Zomp.SyncMethodGenerator/Cloning/ClonedMethod.cs deleted file mode 100644 index ccf899f..0000000 --- a/src/Zomp.SyncMethodGenerator/Cloning/ClonedMethod.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace Zomp.SyncMethodGenerator.Cloning; - -/// -/// A method rewritten by a transformation, ready to be emitted into a file of its own. -/// -/// Where the original method sits. -/// Name of the original method. -/// The rewritten method. -/// True to leave the #nullable enable directive out of the file. -/// Diagnostics the transformation reported. -/// True if there are errors in , in which case nothing is emitted. -/// Signature the rewritten method will be emitted with, used to detect colliding overloads. -internal sealed record ClonedMethod( - MethodLocation Location, - string MethodName, - string Implementation, - bool DisableNullable, - EquatableArray Diagnostics, - bool HasErrors, - MethodSignature? Signature) -{ - /// - /// Creates a cloned method from the result of rewriting it. - /// - /// Where the original method sits. - /// Declaration of the original method. - /// Result of rewriting the method, or the extension block which holds it. - /// True to leave the #nullable enable directive out of the file. - /// Diagnostics the transformation reported. - /// A new . - public static ClonedMethod Create( - MethodLocation location, - MethodDeclarationSyntax original, - SyntaxNode rewritten, - bool disableNullable, - ImmutableArray diagnostics) - { - var hasErrors = false; - foreach (var diagnostic in diagnostics) - { - hasErrors |= diagnostic.Descriptor.DefaultSeverity == DiagnosticSeverity.Error; - } - - return new( - location, - original.Identifier.ValueText, - rewritten.ToFullString(), - disableNullable, - diagnostics, - hasErrors, - MethodSignature.Create(rewritten, location, original)); - } -} diff --git a/src/Zomp.SyncMethodGenerator/Cloning/ClonedMethodOutput.cs b/src/Zomp.SyncMethodGenerator/Cloning/ClonedMethodOutput.cs deleted file mode 100644 index 5486ce0..0000000 --- a/src/Zomp.SyncMethodGenerator/Cloning/ClonedMethodOutput.cs +++ /dev/null @@ -1,138 +0,0 @@ -namespace Zomp.SyncMethodGenerator.Cloning; - -/// -/// Emits cloned methods, each into a file of its own, and reports those which cannot be emitted. -/// -internal static class ClonedMethodOutput -{ - /// - /// Longest generated file name before the scope is shortened. Emitted files sit under - /// {project}\obj\{configuration}\{framework}\generated\{generator assembly}\{generator}, - /// which alone is around a hundred characters before the project path is counted. - /// - private const int MaxFileNameLength = 100; - - /// - /// Adds the output of the generator: a file for each method, and the diagnostics of each. - /// - /// Context of the generator. - /// Methods to emit. - /// Reported against each method whose signature another method also produces. - public static void Register( - IncrementalGeneratorInitializationContext context, - IncrementalValuesProvider methods, - DiagnosticDescriptor collision) - { - var sourceTexts = methods - .Select(static (m, _) => GenerateSource(m)) - .WithTrackingName("GenerateSource"); - - // Signatures produced more than once. Emitting them all would declare the same member - // twice, so the methods behind them are reported instead of generated. - var collidingSignatures = methods - .Select(static (m, _) => m.Signature?.Key) - .Collect() - .Select(static (keys, _) => MethodSignature.FindCollisions(keys)); - - context.RegisterSourceOutput( - sourceTexts.Combine(collidingSignatures), - (spc, pair) => - { - var (source, colliding) = pair; - - foreach (var diagnostic in source.Method.Diagnostics) - { - spc.ReportDiagnostic(diagnostic); - } - - if (source.Method.Signature is { } signature - && colliding.AsImmutableArray().Contains(signature.Key, StringComparer.Ordinal)) - { - spc.ReportDiagnostic(signature.ToDiagnostic(collision)); - return; - } - - if (!source.Method.HasErrors) - { - spc.AddSource(source.Path, SourceText.From(source.Content, Encoding.UTF8)); - } - }); - } - - private static (ClonedMethod Method, string Path, string Content) GenerateSource(ClonedMethod m) - { - var location = m.Location; - - var scope = $"{string.Join(".", location.Namespaces)}" + - $".{string.Join(".", location.Parents.Select(static p => p.ScopeName))}" + - (location.IsCSharp14Extension ? ".ext" : string.Empty); - - var method = m.MethodName + (location.Index == 1 ? string.Empty : "_" + location.Index); - - var sourcePath = BuildFileName(scope, method); - - var source = ClonedMethodSource.Generate(m); - - return (m, sourcePath, source); - } - - /// - /// Builds the file name a generated method is emitted under, shortening it when the - /// namespace and containing type chain make it long enough to be a problem. Emitted files - /// live several directories deep inside the intermediate output path, so a long name here - /// can carry the full path past the limit the file system accepts. - /// - /// - /// The scope is what gets shortened, since the method name is what someone reads the file - /// name for. A hash of the untruncated name keeps distinct methods in distinct files. - /// - private static string BuildFileName(string scope, string method) - { - const string extension = ".g.cs"; - - var fileName = $"{scope}.{method}{extension}"; - - if (fileName.Length <= MaxFileNameLength) - { - return fileName; - } - - var hash = Hash(fileName); - - // separator, hash, dot, extension - var fixedLength = 1 + hash.Length + 1 + extension.Length; - - var forScope = MaxFileNameLength - fixedLength - method.Length; - - if (forScope > 0) - { - return $"{scope[..Math.Min(scope.Length, forScope)]}_{hash}.{method}{extension}"; - } - - // The method name alone fills the budget, so it has to be shortened as well. - return $"_{hash}.{method[..(MaxFileNameLength - fixedLength)]}{extension}"; - } - - /// - /// FNV-1a. Short, dependency free, and stable across processes and runtimes, which - /// is not. - /// - private static string Hash(string value) - { - const uint offsetBasis = 2166136261; - const uint prime = 16777619; - - var hash = offsetBasis; - - unchecked - { - foreach (var c in value) - { - hash ^= c; - hash *= prime; - } - } - - return hash.ToString("x8", System.Globalization.CultureInfo.InvariantCulture); - } -} diff --git a/src/Zomp.SyncMethodGenerator/Cloning/ClonedMethodSource.cs b/src/Zomp.SyncMethodGenerator/Cloning/ClonedMethodSource.cs deleted file mode 100644 index 870b0a8..0000000 --- a/src/Zomp.SyncMethodGenerator/Cloning/ClonedMethodSource.cs +++ /dev/null @@ -1,146 +0,0 @@ -namespace Zomp.SyncMethodGenerator.Cloning; - -/// -/// Builds the source file a cloned method is emitted in, declaring again, as partial, each -/// namespace and type the original method was nested in. -/// -internal static class ClonedMethodSource -{ - /// - /// Builds the source file for a method. - /// - /// The method. - /// Text of the file. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0072:Add missing cases", Justification = "Only deal with selected SyntaxKinds")] - public static string Generate(ClonedMethod method) - { - static string GetKeyword(SyntaxKind sk) => sk switch - { - SyntaxKind.PublicKeyword => "public", - SyntaxKind.InternalKeyword => "internal", - SyntaxKind.PrivateKeyword => "private", - SyntaxKind.SealedKeyword => "sealed", - SyntaxKind.ProtectedKeyword => "protected", - SyntaxKind.StaticKeyword => "static", - SyntaxKind.AbstractKeyword => "abstract", - _ => throw new InvalidOperationException($"{sk} is not supported"), - }; - - var location = method.Location; - - // Handle namespaces - var sbBegin = new StringBuilder(); - var sbEnd = new StringBuilder(); - var i = 0; - - if (!location.IsNamespaceFileScoped) - { - foreach (var @namespace in location.Namespaces) - { - var indent = new string(' ', 4 * i); - _ = sbBegin.Append($$""" -{{indent}}namespace {{@namespace}} -{{indent}}{ - -"""); - _ = sbEnd.Insert(0, $$""" -{{indent}}} - -"""); - ++i; - } - } - - // Directives which were written inside the namespace are emitted inside it, at the - // innermost level. One which names a namespace relatively still resolves from there. - foreach (var @using in location.InnerUsings) - { - _ = sbBegin.Append($$""" -{{new string(' ', 4 * i)}}{{@using}} - -"""); - } - - if (!location.InnerUsings.IsEmpty) - { - _ = sbBegin.Append(""" - - -"""); - } - - // Handle classes - foreach (var parent in location.Parents) - { - var indent = new string(' ', 4 * i); - - var modifiers = string.Join(string.Empty, parent.Modifiers.Select(z => GetKeyword((SyntaxKind)z) + " ")); - var parentType = parent.MethodParent switch - { - MethodParent.Class => "class", - MethodParent.Interface => "interface", - MethodParent.Struct => "struct", - MethodParent.Record => "record" - + parent.ClassOrStructKeyword.Kind() switch - { - SyntaxKind.StructKeyword => " struct", - SyntaxKind.ClassKeyword => " class", - _ => string.Empty, - }, - _ => throw new NotImplementedException("Cannot handle the parent of the method"), - }; - - var classDeclarationLine = $"{modifiers}partial {parentType} {parent.ParentName}{(parent.TypeParameterListSyntax.IsEmpty ? string.Empty - : "<" + string.Join(", ", parent.TypeParameterListSyntax) + ">")}"; - - _ = sbBegin.Append($$""" -{{indent}}{{classDeclarationLine}} -{{indent}}{ - -"""); - _ = sbEnd.Insert(0, $$""" -{{indent}}} - -"""); - ++i; - } - - var sbOuterUsings = new StringBuilder(); - foreach (var @using in location.OuterUsings) - { - if (sbOuterUsings.Length == 0) - { - // Separates the directives from the header above them. - _ = sbOuterUsings.Append(""" - - - -"""); - } - - _ = sbOuterUsings.Append($$""" -{{@using}} - -"""); - } - - var beforeNamespace = $""" -// {(method.DisableNullable ? string.Empty : """ - -#nullable enable -""")}{sbOuterUsings} -"""; - - return location.IsNamespaceFileScoped ? $$""" -{{beforeNamespace}} -namespace {{location.Namespaces.First()}}; -{{sbBegin}}{{new string(' ', 4 * i)}}{{method.Implementation.Trim()}} -{{sbEnd}} -""" - : $$""" -{{beforeNamespace}} -{{sbBegin}}{{new string(' ', 4 * i)}}{{method.Implementation.Trim()}} -{{sbEnd}} -"""; - } -} diff --git a/src/Zomp.SyncMethodGenerator/Cloning/CloningRewriter.cs b/src/Zomp.SyncMethodGenerator/Cloning/CloningRewriter.cs deleted file mode 100644 index 05e74cf..0000000 --- a/src/Zomp.SyncMethodGenerator/Cloning/CloningRewriter.cs +++ /dev/null @@ -1,432 +0,0 @@ -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Zomp.SyncMethodGenerator.Cloning; - -/// -/// Rewrites a method so that a copy of it compiles in a file of its own. Every type and static -/// member the method names is fully qualified, so that it resolves to the same symbol in the file -/// the copy is emitted to as in the file the method was written in. A transformation derives from -/// this class to change what the copy does. -/// -/// The semantic model. -/// The method declaration to rewrite; other method declarations are dropped. -internal abstract class CloningRewriter(SemanticModel semanticModel, MethodDeclarationSyntax targetMethod) : CSharpSyntaxRewriter -{ - private static readonly SymbolDisplayFormat GlobalDisplayFormat = new( - globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included, - typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, - genericsOptions: SymbolDisplayGenericsOptions.None, - miscellaneousOptions: - SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | - SymbolDisplayMiscellaneousOptions.UseSpecialTypes); - - private static readonly SymbolDisplayFormat GlobalDisplayFormatWithTypeParameters = new( - globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included, - typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, - genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, - miscellaneousOptions: - SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | - SymbolDisplayMiscellaneousOptions.UseSpecialTypes); - - /// - /// Gets the semantic model of the tree the method is in. - /// - protected SemanticModel SemanticModel { get; } = semanticModel; - - /// - public override SyntaxNode? VisitArgument(ArgumentSyntax node) - { - var @base = (ArgumentSyntax)base.VisitArgument(node)!; - - // Handles nameof(Type) - return GetSymbol(node.Expression) is ITypeSymbol typeSymbol && !TypeAlreadyQualified(typeSymbol) - ? @base.WithExpression(ProcessSymbol(typeSymbol)).WithTriviaFrom(@base) - : @base; - } - - /// - public override SyntaxNode? VisitArrayType(ArrayTypeSyntax node) - { - var @base = (ArrayTypeSyntax)base.VisitArrayType(node)!; - var elementType = TypeAlreadyQualified(node.ElementType) - ? @base.ElementType - : ProcessType(@base.ElementType); - return @base.WithElementType(elementType).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitAttribute(AttributeSyntax node) - { - var @base = (AttributeSyntax)base.VisitAttribute(node)!; - - if (GetSymbol(node.Name) is not IMethodSymbol ms) - { - return @base; - } - - var retval = @base.WithName(ProcessSymbol(ms.ContainingType)); - return retval; - } - - /// - public override SyntaxNode? VisitBinaryExpression(BinaryExpressionSyntax node) - { - var @base = (BinaryExpressionSyntax)base.VisitBinaryExpression(node)!; - - if (@base.OperatorToken.IsKind(SyntaxKind.IsKeyword) || @base.OperatorToken.IsKind(SyntaxKind.AsKeyword)) - { - if (GetSymbol(node.Left) is IFieldSymbol leftSymbol) - { - @base = @base.WithLeft(ProcessSymbol(leftSymbol).WithTriviaFrom(node.Left)); - } - - if (GetSymbol(node.Right) is ISymbol symbol) - { - @base = @base.WithRight(ProcessSymbol(symbol).WithTriviaFrom(node.Right)); - } - - return @base.WithTriviaFrom(@base); - } - - return @base; - } - - /// - public override SyntaxNode? VisitCastExpression(CastExpressionSyntax node) - { - var @base = (CastExpressionSyntax)base.VisitCastExpression(node)!; - return TypeAlreadyQualified(node.Type) ? @base : @base.WithType(ProcessType(node.Type)).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitCatchDeclaration(CatchDeclarationSyntax node) - { - var @base = (CatchDeclarationSyntax)base.VisitCatchDeclaration(node)!; - return @base.WithType(ProcessType(node.Type)).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitConstantPattern(ConstantPatternSyntax node) - { - var @base = (ConstantPatternSyntax)base.VisitConstantPattern(node)!; - return node.Expression switch - { - LiteralExpressionSyntax or MemberAccessExpressionSyntax => @base, - _ => SemanticModel.GetTypeInfo(node.Expression).Type is { } type - ? @base.WithExpression(ProcessSymbol(type).WithTriviaFrom(@base)) - : @base, - }; - } - - /// - public override SyntaxNode? VisitDeclarationExpression(DeclarationExpressionSyntax node) - { - var @base = (DeclarationExpressionSyntax)base.VisitDeclarationExpression(node)!; - return TypeAlreadyQualified(node.Type) ? @base : @base.WithType(ProcessType(node.Type)).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitDeclarationPattern(DeclarationPatternSyntax node) - { - var @base = (DeclarationPatternSyntax)base.VisitDeclarationPattern(node)!; - return TypeAlreadyQualified(node.Type) ? @base : @base.WithType(ProcessType(node.Type)).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitForEachStatement(ForEachStatementSyntax node) - { - var @base = (ForEachStatementSyntax)base.VisitForEachStatement(node)!; - return TypeAlreadyQualified(node.Type) ? @base : @base.WithType(ProcessType(node.Type)).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitGenericName(GenericNameSyntax node) - { - var @base = (GenericNameSyntax)base.VisitGenericName(node)!; - - if (GetSymbol(node) is not INamedTypeSymbol symbol) - { - return @base; - } - - var identifier = symbol switch - { - { ContainingSymbol: INamedTypeSymbol { IsGenericType: true } parentSymbol } - => parentSymbol.ToDisplayString(GlobalDisplayFormatWithTypeParameters) + "." + symbol.Name, - _ => symbol.ToDisplayString(GlobalDisplayFormat), - }; - - return @base.WithIdentifier(Identifier(identifier)).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) - { - var @base = (IdentifierNameSyntax)base.VisitIdentifierName(node)!; - - if (node.Parent is not MemberAccessExpressionSyntax) - { - var symbol = GetSymbol(node); - if (symbol is { IsStatic: true, ContainingType: { } containingType } memberSymbol) - { - if (symbol is IFieldSymbol or IMethodSymbol { MethodKind: not MethodKind.LocalFunction }) - { - var typeString = containingType.ToDisplayString(GlobalDisplayFormatWithTypeParameters); - return @base.WithIdentifier(Identifier($"{typeString}.{memberSymbol.Name}")).WithTriviaFrom(node); - } - } - } - - if (node.Parent is TypeArgumentListSyntax) - { - return ProcessType(node); - } - - return @base; - } - - /// - /// Parenthesizes every interpolated expression. A fully qualified name contains ::, - /// whose first colon would otherwise start a format string. - /// - /// The interpolation. - /// The interpolation with its expression parenthesized. - public override SyntaxNode? VisitInterpolation(InterpolationSyntax node) - { - var @base = (InterpolationSyntax)base.VisitInterpolation(node)!; - if (@base.Expression is not ParenthesizedExpressionSyntax) - { - var newExpression = ParenthesizedExpression(@base.Expression); - @base = @base.WithExpression(newExpression); - } - - return @base; - } - - /// - public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node) - { - var @base = (MemberAccessExpressionSyntax)base.VisitMemberAccessExpression(node)!; - - // Rewrite static invocation (eg. File.ReadAllText) - if (GetSymbol(node.Expression) is ITypeSymbol && node.Expression is TypeSyntax type) - { - var newType = ProcessType(type); - if (newType != type) - { - @base = @base.WithExpression(newType); - } - } - - return @base; - } - - /// - public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node) - => node == targetMethod ? base.VisitMethodDeclaration(node) : null; - - /// - public override SyntaxNode? VisitNullableType(NullableTypeSyntax node) - { - var @base = (NullableTypeSyntax)base.VisitNullableType(node)!; - - return TypeAlreadyQualified(node.ElementType) ? @base : @base.WithElementType(ProcessType(@base.ElementType)).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitObjectCreationExpression(ObjectCreationExpressionSyntax node) - { - var @base = (ObjectCreationExpressionSyntax)base.VisitObjectCreationExpression(node)!; - - if (SemanticModel.GetTypeInfo(node).Type is not { } t - || t is INamedTypeSymbol { IsGenericType: true }) - { - return @base; - } - - var newType = ProcessSymbol(t); - return newType == node.Type ? @base : @base.WithType(newType); - } - - /// - public override SyntaxNode? VisitParameter(ParameterSyntax node) - { - var @base = (ParameterSyntax)base.VisitParameter(node)!; - - return node.Type is null || TypeAlreadyQualified(node.Type) ? @base - : @base.WithType(ProcessType(node.Type)).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitQualifiedName(QualifiedNameSyntax node) - { - var @base = (QualifiedNameSyntax)base.VisitQualifiedName(node)!; - - return @base.Right is GenericNameSyntax ? @base.Right : (SyntaxNode)ProcessType(node); - } - - /// - public override SyntaxNode? VisitTupleType(TupleTypeSyntax node) - { - var @base = (TupleTypeSyntax)base.VisitTupleType(node)!; - - var newTuples = new List(); - foreach (var t in node.Elements.Zip(@base.Elements, (original, visited) => (original, visited))) - { - var newType = TypeAlreadyQualified(t.original.Type) ? t.visited.Type : ProcessType(t.original.Type); - newTuples.Add(TupleElement(newType, t.original.Identifier)); - } - - return @base.WithElements(SeparatedList(newTuples, node.Elements.GetSeparators())); - } - - /// - public override SyntaxNode? VisitTypeConstraint(TypeConstraintSyntax node) - { - var @base = (TypeConstraintSyntax)base.VisitTypeConstraint(node)!; - var newType = ProcessType(@base.Type); - return newType == @base.Type ? @base : @base.WithType(newType).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitTypeOfExpression(TypeOfExpressionSyntax node) - { - var @base = (TypeOfExpressionSyntax)base.VisitTypeOfExpression(node)!; - return TypeAlreadyQualified(node.Type) ? @base : @base.WithType(ProcessType(node.Type)).WithTriviaFrom(@base); - } - - /// - public override SyntaxNode? VisitVariableDeclaration(VariableDeclarationSyntax node) - { - var @base = (VariableDeclarationSyntax)base.VisitVariableDeclaration(node)!; - - var type = node.Type; - var newType = @base.Type; - - if (newType == type || - (newType is IdentifierNameSyntax { Identifier.ValueText: { } newTypeString } - && type is IdentifierNameSyntax { Identifier.ValueText: { } typeString } - && newTypeString == typeString)) - { - // not replaced - newType = ProcessType(type); - - if (newType == type) - { - return @base; - } - } - - return @base.WithType(newType).WithTriviaFrom(@base); - } - - /// - /// Prefixes a fully qualified name with the global alias. - /// - /// Fully qualified name. - /// The name, prefixed with global::. - protected static string Global(string type) => $"global::{type}"; - - /// - /// Removes the preprocessor directives, and the text they disable, from the trivia which - /// leads a method. The copy is emitted without whatever surrounded the original. - /// - /// Leading trivia of the method. - /// The trivia without preprocessor directives. - protected static SyntaxTriviaList RemovePreprocessorDirectives(SyntaxTriviaList trivia) - { - static bool Preprocessors(SyntaxTrivia st) - => st.IsKind(SyntaxKind.IfDirectiveTrivia) - || st.IsKind(SyntaxKind.ElifDirectiveTrivia) - || st.IsKind(SyntaxKind.ElseDirectiveTrivia) - || st.IsKind(SyntaxKind.EndIfDirectiveTrivia) - || st.IsKind(SyntaxKind.RegionDirectiveTrivia) - || st.IsKind(SyntaxKind.EndRegionDirectiveTrivia) - || st.IsKind(SyntaxKind.DisabledTextTrivia); - - while (trivia.FirstOrDefault(Preprocessors) is { } preprocessor - && preprocessor != default) - { - trivia = trivia.Remove(preprocessor); - } - - return trivia; - } - - /// - /// Checks whether a type is written in a form which its own visitor qualifies. - /// - /// The type. - /// True if the type needs nothing more. - protected static bool TypeAlreadyQualified(TypeSyntax type) - => type is NullableTypeSyntax or GenericNameSyntax or TupleTypeSyntax or ArrayTypeSyntax or QualifiedNameSyntax; - - /// - /// Gets the symbol a node refers to. - /// - /// The node. - /// The symbol, or null if the node does not bind to one. - protected ISymbol? GetSymbol(SyntaxNode node) => SemanticModel.GetSymbolInfo(node).Symbol; - - /// - /// Gets the fully qualified name of a type. - /// - /// The type, or any other symbol, which is named as it is. - /// The name. - protected string MakeType(ISymbol symbol) - => symbol switch - { - INamedTypeSymbol nts => MapTypeName(nts) ?? symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), - _ => symbol.Name, - }; - - /// - /// Names a symbol so that it resolves wherever the copy is emitted. - /// - /// The symbol. - /// The name. - protected SimpleNameSyntax ProcessSymbol(ISymbol typeSymbol) => MapSymbol(typeSymbol) ?? typeSymbol switch - { - INamedTypeSymbol nts => IdentifierName(MakeType(nts)), - IArrayTypeSymbol ats => IdentifierName(MakeType(ats.ElementType) + $"[{new string(',', ats.Rank - 1)}]"), - IFieldSymbol fs => IdentifierName(MakeType(fs.Type) + '.' + fs.Name), - _ => IdentifierName(typeSymbol.Name), - }; - - /// - /// Qualifies a type written as a simple or qualified name. - /// - /// The type. - /// The qualified type, or the type itself if it needs nothing. - protected TypeSyntax ProcessType(TypeSyntax typeSyntax) => typeSyntax switch - { - IdentifierNameSyntax { Identifier.ValueText: "var" } => typeSyntax, - IdentifierNameSyntax or QualifiedNameSyntax => ProcessSyntaxUsingSymbol(typeSyntax), - _ => typeSyntax, - }; - - /// - /// Gives a transformation the chance to name a symbol differently in the copy. - /// - /// The symbol. - /// The name to use, or null to use the fully qualified name. - protected virtual SimpleNameSyntax? MapSymbol(ISymbol symbol) => null; - - /// - /// Gives a transformation the chance to substitute one type for another wherever a type is - /// named by its fully qualified name. - /// - /// The type. - /// The name to use, or null to use the fully qualified name. - protected virtual string? MapTypeName(INamedTypeSymbol symbol) => null; - - private static bool TypeAlreadyQualified(ITypeSymbol type) - => type is INamedTypeSymbol namedType - && namedType is { IsGenericType: true }; - - private TypeSyntax ProcessSyntaxUsingSymbol(TypeSyntax typeSyntax) - { - var typeSymbol = SemanticModel.GetTypeInfo(typeSyntax).Type; - return typeSymbol is null ? typeSyntax : ProcessSymbol(typeSymbol).WithTriviaFrom(typeSyntax); - } -} diff --git a/src/Zomp.SyncMethodGenerator/Cloning/MethodLocation.cs b/src/Zomp.SyncMethodGenerator/Cloning/MethodLocation.cs deleted file mode 100644 index 158cc23..0000000 --- a/src/Zomp.SyncMethodGenerator/Cloning/MethodLocation.cs +++ /dev/null @@ -1,183 +0,0 @@ -namespace Zomp.SyncMethodGenerator.Cloning; - -/// -/// Everything around a method which a copy of it needs in order to compile in a file of its own: -/// the namespaces and types it is nested in, and the using directives in scope where it was written. -/// -/// Position of the method among the members of its type which share its name, starting at 1. -/// Namespaces the method is under, outermost first. -/// Using directives the source file declares outside its namespace. -/// Using directives the source file declares inside its namespace. -/// True if the namespace is file scoped. -/// True if the method is a member of a C# 14 extension block. -/// Types the method is nested in, outermost first. -internal sealed record MethodLocation( - int Index, - EquatableArray Namespaces, - EquatableArray OuterUsings, - EquatableArray InnerUsings, - bool IsNamespaceFileScoped, - bool IsCSharp14Extension, - EquatableArray Parents) -{ - /// - /// Gets the namespaces and types the method is nested in, joined with dots. - /// - public string Scope => string.Join(".", Namespaces.Concat(Parents.Select(static p => p.ScopeName))); - - /// - /// Locates a method. - /// - /// Declaration of the method. - /// Symbol of the method. - /// Where the method sits. - /// Node to rewrite: the method, or the extension block which holds it. - /// False when the method is not nested in a type which can be declared again as partial. - public static bool TryCreate( - MethodDeclarationSyntax method, - IMethodSymbol symbol, - [NotNullWhen(true)] out MethodLocation? location, - [NotNullWhen(true)] out SyntaxNode? root) - { - location = null; - root = null; - - var parents = ImmutableArray.CreateBuilder(); - SyntaxNode? node = method; -#if ROSLYN_5_0_OR_GREATER - ExtensionBlockDeclarationSyntax? extensionParent = null; -#endif - while (node.Parent is not null) - { - node = node.Parent; -#if ROSLYN_5_0_OR_GREATER - if (node is ExtensionBlockDeclarationSyntax eds) - { - extensionParent = eds; - continue; - } -#endif - - MethodParentDeclaration? mpd = node switch - { - ClassDeclarationSyntax o => new(MethodParent.Class, o.Identifier, o.Modifiers, o.TypeParameterList), - StructDeclarationSyntax o => new(MethodParent.Struct, o.Identifier, o.Modifiers, o.TypeParameterList), - RecordDeclarationSyntax o => new(MethodParent.Record, o.Identifier, o.Modifiers, o.TypeParameterList, o.ClassOrStructKeyword), - InterfaceDeclarationSyntax o => new(MethodParent.Interface, o.Identifier, o.Modifiers, o.TypeParameterList), - _ => null, - }; - - if (mpd is null) - { - break; - } - - parents.Insert(0, mpd); - } - - if (parents.Count == 0) - { - return false; - } - - var isNamespaceFileScoped = false; - var namespaces = ImmutableArray.CreateBuilder(); - - // Documentation comments are copied across verbatim, and a cref in one is resolved - // against the file it lands in rather than the file it was written in. Without the - // using directives which were in scope where it was written, every cref which relied on - // one goes unresolved, which a project building with warnings as errors reads as a - // build failure. The directives are kept on the side of the namespace they were - // declared on, since one declared inside a namespace may name it only relatively. - var outerUsings = ImmutableArray.CreateBuilder(); - var innerUsings = ImmutableArray.CreateBuilder(); - - while (node is not null and not CompilationUnitSyntax) - { - switch (node) - { - case NamespaceDeclarationSyntax nds: - namespaces.Insert(0, nds.Name.ToString()); - InsertUsings(innerUsings, nds.Usings); - break; - case FileScopedNamespaceDeclarationSyntax file: - namespaces.Add(file.Name.ToString()); - InsertUsings(innerUsings, file.Usings); - isNamespaceFileScoped = true; - break; - default: - throw new InvalidOperationException($"Cannot handle {node}"); - } - - node = node.Parent; - } - - if (node is CompilationUnitSyntax compilationUnit) - { - InsertUsings(outerUsings, compilationUnit.Usings); - } - -#if ROSLYN_5_0_OR_GREATER - var isCSharp14Extension = extensionParent is not null; - root = extensionParent ?? (SyntaxNode)method; -#else - var isCSharp14Extension = false; - root = method; -#endif - - location = new( - GetIndex(symbol), - namespaces.ToImmutable(), - outerUsings.ToImmutable(), - innerUsings.ToImmutable(), - isNamespaceFileScoped, - isCSharp14Extension, - parents.ToImmutable()); - - return true; - } - - /// - /// Counts the members of the containing type which share the method's name and come before - /// it, so that overloads are told apart in the names of the files they are emitted to. - /// - /// Symbol of the method. - /// Position of the method among its namesakes, starting at 1. - private static int GetIndex(IMethodSymbol symbol) - { - var index = 1; - - if (symbol.ContainingType is { } containingType) - { - foreach (var member in containingType.GetMembers()) - { - if (member.Equals(symbol, SymbolEqualityComparer.Default)) - { - break; - } - - if (member.Name.Equals(symbol.Name, StringComparison.Ordinal)) - { - ++index; - } - } - } - - return index; - } - - /// - /// Records the directives, innermost first, so that walking outwards from the method builds - /// them up in the order they were written. - /// - /// Collected directives. - /// Directives declared at one level. - private static void InsertUsings(ImmutableArray.Builder destination, SyntaxList usings) - { - var index = 0; - foreach (var @using in usings) - { - destination.Insert(index++, @using.ToString()); - } - } -} diff --git a/src/Zomp.SyncMethodGenerator/Cloning/MethodParent.cs b/src/Zomp.SyncMethodGenerator/Cloning/MethodParent.cs deleted file mode 100644 index f7e78ce..0000000 --- a/src/Zomp.SyncMethodGenerator/Cloning/MethodParent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Zomp.SyncMethodGenerator.Cloning; - -internal enum MethodParent -{ - Class, - Struct, - Record, - Interface, -} diff --git a/src/Zomp.SyncMethodGenerator/Cloning/MethodParentDeclaration.cs b/src/Zomp.SyncMethodGenerator/Cloning/MethodParentDeclaration.cs deleted file mode 100644 index 6b5d538..0000000 --- a/src/Zomp.SyncMethodGenerator/Cloning/MethodParentDeclaration.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Zomp.SyncMethodGenerator.Cloning; - -/// -/// Represents a type a is nested in. -/// -/// Type of container. -/// Class name. -/// A list of modifiers. -/// A list of type parameters. -/// Indicates whether struct or class are explicitly specified for a record. -internal sealed record MethodParentDeclaration(MethodParent MethodParent, string ParentName, EquatableArray Modifiers, EquatableArray TypeParameterListSyntax, SyntaxToken ClassOrStructKeyword) -{ - public MethodParentDeclaration(MethodParent methodParent, SyntaxToken parentName, SyntaxTokenList modifiers, TypeParameterListSyntax? typeParameterList, SyntaxToken classOrStructKeyword = default) - : this( - methodParent, - parentName.ValueText, - modifiers.Select(z => (ushort)z.RawKind).Where(z => z != (ushort)SyntaxKind.PartialKeyword).ToImmutableArray(), - (typeParameterList is null ? [] : typeParameterList.Parameters.Select(z => z.Identifier.ValueText)).ToImmutableArray(), - classOrStructKeyword) - { - } - - /// - /// Gets the name of the type with its type parameters, so that Class, Class{T} - /// and Class{T,T2} stay distinct. - /// - public string ScopeName => TypeParameterListSyntax.IsEmpty - ? ParentName - : ParentName + "{" + string.Join(",", TypeParameterListSyntax) + "}"; -} diff --git a/src/Zomp.SyncMethodGenerator/Cloning/MethodSignature.cs b/src/Zomp.SyncMethodGenerator/Cloning/MethodSignature.cs deleted file mode 100644 index 881b916..0000000 --- a/src/Zomp.SyncMethodGenerator/Cloning/MethodSignature.cs +++ /dev/null @@ -1,89 +0,0 @@ -namespace Zomp.SyncMethodGenerator.Cloning; - -/// -/// The signature a cloned method will be emitted with, and where the method it came from was -/// written. Two methods sharing a cannot both be emitted: the compiler would -/// see the same member declared twice. -/// -/// Fully qualified signature of the method which will be emitted. -/// File path of the method being cloned. -/// Text span of the method being cloned. -/// Line span of the method being cloned. -internal sealed record MethodSignature(string Key, string FilePath, TextSpan TextSpan, LinePositionSpan LineSpan) -{ - /// - /// Compares by alone. The spans move whenever anything above the method is - /// edited, and letting that reach the incremental pipeline would defeat caching for a method - /// whose generated output has not changed at all. - /// - /// Signature to compare against. - /// True if both describe the same emitted member. - public bool Equals(MethodSignature? other) - => other is not null && string.Equals(Key, other.Key, StringComparison.Ordinal); - - /// - public override int GetHashCode() => Key.GetHashCode(); - - /// - /// Describes the method which is about to be emitted, precisely enough to tell whether two - /// of them would declare the same member. The rewritten declaration is used rather than the - /// original symbol, so the comparison sees exactly what the compiler will see - parameters - /// already dropped, types already substituted. - /// - /// Result of rewriting the method. - /// Where the original method sits. - /// Method being cloned, for the location to report against. - /// The signature, or null when the rewritten method could not be located. - public static MethodSignature? Create(SyntaxNode rewritten, MethodLocation location, MethodDeclarationSyntax original) - { - var method = rewritten as MethodDeclarationSyntax - ?? rewritten.DescendantNodes().OfType().FirstOrDefault(); - - if (method is null) - { - return null; - } - - // Arity by count rather than by name, since overloads which differ only in what they - // call their type parameters still declare the same member. - var arity = method.TypeParameterList?.Parameters.Count ?? 0; - var arityMarker = arity > 0 ? "`" + arity.ToString(System.Globalization.CultureInfo.InvariantCulture) : string.Empty; - - var parameters = string.Join(",", method.ParameterList.Parameters.Select(static p => p.Type?.ToString() ?? string.Empty)); - - var key = $"{location.Scope}.{method.Identifier.ValueText}{arityMarker}({parameters})"; - - var sourceLocation = original.GetLocation(); - - return new(key, sourceLocation.SourceTree?.FilePath ?? string.Empty, sourceLocation.SourceSpan, sourceLocation.GetLineSpan().Span); - } - - /// - /// Picks out the signatures produced by more than one method. Emitting all of them would - /// declare the same member twice. - /// - /// Signature of every method being generated. - /// The signatures which appear more than once. - public static EquatableArray FindCollisions(ImmutableArray keys) - { - var colliding = keys - .Where(static key => key is not null) - .GroupBy(static key => key!, StringComparer.Ordinal) - .Where(static group => group.Count() > 1) - .Select(static group => group.Key); - -#if ROSLYN_4_12_OR_GREATER - return new([.. colliding]); -#else - return new(ImmutableArray.CreateRange(colliding)); -#endif - } - - /// - /// Builds the diagnostic reported when this signature is produced more than once. - /// - /// Descriptor of the diagnostic. - /// A new . - public ReportedDiagnostic ToDiagnostic(DiagnosticDescriptor descriptor) - => new(descriptor, FilePath, TextSpan, LineSpan, Key); -} diff --git a/src/Zomp.SyncMethodGenerator/Helpers/EquatableArray.cs b/src/Zomp.SyncMethodGenerator/Helpers/EquatableArray.cs deleted file mode 100644 index 9d30b5b..0000000 --- a/src/Zomp.SyncMethodGenerator/Helpers/EquatableArray.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; - -namespace Zomp.SyncMethodGenerator.Helpers; - -/// -/// An immutable, equatable array. This is equivalent to but with value equality support. -/// -/// The type of values in the array. -/// -/// Modified from: https://github.com/dotnet/runtime/issues/77183#issuecomment-1284577055. -/// Remove this struct when the issue above is resolved. -/// -/// -/// Initializes a new instance of the struct. -/// -/// The input to wrap. -[ExcludeFromCodeCoverage] -internal readonly struct EquatableArray(ImmutableArray array) : IEquatable>, IEnumerable - where T : IEquatable -{ - /// - /// The underlying array. - /// - private readonly T[]? array = Unsafe.As, T[]?>(ref array); - - /// - /// Gets a value indicating whether the current array is empty. - /// - public bool IsEmpty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => AsImmutableArray().IsEmpty; - } - - /// - /// Gets a reference to an item at a specified position within the array. - /// - /// The index of the item to retrieve a reference to. - /// A reference to an item at a specified position within the array. - public ref readonly T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref AsImmutableArray().ItemRef(index); - } - - /// - /// Implicitly converts an to . - /// - /// An instance from a given . - public static implicit operator EquatableArray(ImmutableArray array) => FromImmutableArray(array); - - /// - /// Implicitly converts an to . - /// - /// An instance from a given . - public static implicit operator ImmutableArray(EquatableArray array) => array.AsImmutableArray(); - - /// - /// Checks whether two values are the same. - /// - /// The first value. - /// The second value. - /// Whether and are equal. - public static bool operator ==(EquatableArray left, EquatableArray right) => left.Equals(right); - - /// - /// Checks whether two values are not the same. - /// - /// The first value. - /// The second value. - /// Whether and are not equal. - public static bool operator !=(EquatableArray left, EquatableArray right) => !left.Equals(right); - - /// - /// Creates an instance from a given . - /// - /// The input instance. - /// An instance from a given . - public static EquatableArray FromImmutableArray(ImmutableArray array) => new(array); - - /// - public bool Equals(EquatableArray array) => AsSpan().SequenceEqual(array.AsSpan()); - - /// - public override bool Equals([NotNullWhen(true)] object? obj) => obj is EquatableArray array && Equals(this, array); - - /// - public override int GetHashCode() - { - if (this.array is not T[] array) - { - return 0; - } - - HashCode hashCode = default; - - foreach (var item in array) - { - hashCode.Add(item); - } - - return hashCode.ToHashCode(); - } - - /// - /// Gets an instance from the current . - /// - /// The from the current . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ImmutableArray AsImmutableArray() => Unsafe.As>(ref Unsafe.AsRef(in array)); - - /// - /// Returns a wrapping the current items. - /// - /// A wrapping the current items. - public ReadOnlySpan AsSpan() => AsImmutableArray().AsSpan(); - - /// - /// Copies the contents of this instance to a mutable array. - /// - /// The newly instantiated array. - public T[] ToArray() => [.. AsImmutableArray()]; - - /// - /// Gets an value to traverse items in the current array. - /// - /// An value to traverse items in the current array. - public ImmutableArray.Enumerator GetEnumerator() => AsImmutableArray().GetEnumerator(); - - /// - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)AsImmutableArray()).GetEnumerator(); - - /// - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)AsImmutableArray()).GetEnumerator(); -} - -/// -/// Extensions for . -/// -[ExcludeFromCodeCoverage] -internal static class EquatableArray -{ - /// - /// Creates an instance from a given . - /// - /// The type of items in the input array. - /// The input instance. - /// An instance from a given . - public static EquatableArray AsEquatableArray(this ImmutableArray array) - where T : IEquatable => new(array); -} diff --git a/src/Zomp.SyncMethodGenerator/Models/ReportedDiagnostic.cs b/src/Zomp.SyncMethodGenerator/Models/ReportedDiagnostic.cs deleted file mode 100644 index 694aa50..0000000 --- a/src/Zomp.SyncMethodGenerator/Models/ReportedDiagnostic.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Zomp.SyncMethodGenerator.Models; - -/// -/// Basic diagnostic description for reporting diagnostic inside the incremental pipeline. -/// -/// Diagnostic descriptor. -/// File path. -/// Text span. -/// Line span. -/// Trivia. -/// -internal sealed record ReportedDiagnostic(DiagnosticDescriptor Descriptor, string FilePath, TextSpan TextSpan, LinePositionSpan LineSpan, string Trivia) -{ - /// - /// Implicitly converts to . - /// - /// Diagnostic to convert. - public static implicit operator Diagnostic(ReportedDiagnostic diagnostic) - => Diagnostic.Create( - descriptor: diagnostic.Descriptor, - location: Location.Create(diagnostic.FilePath, diagnostic.TextSpan, diagnostic.LineSpan), - messageArgs: [diagnostic.Trivia]); - - /// - /// Creates a new from and . - /// - /// Descriptor. - /// Location. - /// Trivia. - /// A new . - public static ReportedDiagnostic Create(DiagnosticDescriptor descriptor, Location location, string trivia) - { - return new(descriptor, location.SourceTree?.FilePath ?? string.Empty, location.SourceSpan, location.GetLineSpan().Span, trivia); - } -} diff --git a/src/Zomp.SyncMethodGenerator/Zomp.SyncMethodGenerator.csproj b/src/Zomp.SyncMethodGenerator/Zomp.SyncMethodGenerator.csproj index a8c2e83..68316c6 100644 --- a/src/Zomp.SyncMethodGenerator/Zomp.SyncMethodGenerator.csproj +++ b/src/Zomp.SyncMethodGenerator/Zomp.SyncMethodGenerator.csproj @@ -29,6 +29,7 @@ + @@ -45,9 +46,8 @@ - + - diff --git a/tests/Generator.Tests/FileNameTests.cs b/tests/Generator.Tests/FileNameTests.cs deleted file mode 100644 index 38309d7..0000000 --- a/tests/Generator.Tests/FileNameTests.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace Generator.Tests; - -public class FileNameTests -{ - [Fact] - public Task DoNotCollideClassNames() => """ -namespace Test; - -public partial class Class -{ - [CreateSyncVersion] - public async Task MethodAsync() - { - } -} - -public partial class Class -{ - [CreateSyncVersion] - public async Task MethodAsync() - { - } -} - -public partial class Class -{ - [CreateSyncVersion] - public async Task MethodAsync() - { - } -} -""".Verify(sourceType: SourceType.Full); - - [Fact] - public Task ShortenLongFileName() => """ -namespace A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Reasonable; - -public partial class OuterClass -{ - public partial class InnerClass - { - [CreateSyncVersion] - public async Task MethodAsync() - { - } - } -} -""".Verify(sourceType: SourceType.Full); - - [Fact] - public Task ShortenLongFileNameWhenTheMethodNameFillsIt() => """ -namespace A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Reasonable; - -public partial class OuterClass -{ - [CreateSyncVersion] - public async Task ThisMethodHasAnUnreasonablyLongNameWhichOnItsOwnLeavesNoRoomForTheNamespaceOrTheContainingTypeAsync() - { - } -} -""".Verify(sourceType: SourceType.Full); - - [Fact] - public Task ShortenLongFileNameKeepsOverloadsApart() => """ -namespace A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Reasonable; - -public partial class OuterClass -{ - public partial class InnerClass - { - [CreateSyncVersion] - public async Task MethodAsync() - { - } - - [CreateSyncVersion] - public async Task MethodAsync(int i) - { - } - } -} -""".Verify(sourceType: SourceType.Full); -} diff --git a/tests/Generator.Tests/IdentityCloneGenerator.cs b/tests/Generator.Tests/IdentityCloneGenerator.cs index c9f1039..7544b4a 100644 --- a/tests/Generator.Tests/IdentityCloneGenerator.cs +++ b/tests/Generator.Tests/IdentityCloneGenerator.cs @@ -1,5 +1,5 @@ +using Zomp.MethodCloning; using Zomp.SyncMethodGenerator; -using Zomp.SyncMethodGenerator.Cloning; namespace Generator.Tests; diff --git a/tests/Generator.Tests/IdentityRewriter.cs b/tests/Generator.Tests/IdentityRewriter.cs index 459bab3..5d54240 100644 --- a/tests/Generator.Tests/IdentityRewriter.cs +++ b/tests/Generator.Tests/IdentityRewriter.cs @@ -1,5 +1,5 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; -using Zomp.SyncMethodGenerator.Cloning; +using Zomp.MethodCloning; namespace Generator.Tests; diff --git a/tests/Generator.Tests/Snapshots/FileNameTests.DoNotCollideClassNames#Test.Class{T,T2}.MethodAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/FileNameTests.DoNotCollideClassNames#Test.Class{T,T2}.MethodAsync.g.verified.cs deleted file mode 100644 index ec9b3af..0000000 --- a/tests/Generator.Tests/Snapshots/FileNameTests.DoNotCollideClassNames#Test.Class{T,T2}.MethodAsync.g.verified.cs +++ /dev/null @@ -1,10 +0,0 @@ -//HintName: Test.Class{T,T2}.MethodAsync.g.cs -// -#nullable enable -namespace Test; -public partial class Class -{ - public void Method() - { - } -} diff --git a/tests/Generator.Tests/Snapshots/FileNameTests.DoNotCollideClassNames#Test.Class{T}.MethodAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/FileNameTests.DoNotCollideClassNames#Test.Class{T}.MethodAsync.g.verified.cs deleted file mode 100644 index a29686b..0000000 --- a/tests/Generator.Tests/Snapshots/FileNameTests.DoNotCollideClassNames#Test.Class{T}.MethodAsync.g.verified.cs +++ /dev/null @@ -1,10 +0,0 @@ -//HintName: Test.Class{T}.MethodAsync.g.cs -// -#nullable enable -namespace Test; -public partial class Class -{ - public void Method() - { - } -} diff --git a/tests/Generator.Tests/Snapshots/FileNameTests.DoNotCollideClassNames#g.verified.cs b/tests/Generator.Tests/Snapshots/FileNameTests.DoNotCollideClassNames#g.verified.cs deleted file mode 100644 index 654d66b..0000000 --- a/tests/Generator.Tests/Snapshots/FileNameTests.DoNotCollideClassNames#g.verified.cs +++ /dev/null @@ -1,10 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -// -#nullable enable -namespace Test; -public partial class Class -{ - public void Method() - { - } -} diff --git a/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileName#A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Rea_89a6d38e.MethodAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileName#A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Rea_89a6d38e.MethodAsync.g.verified.cs deleted file mode 100644 index 071af7c..0000000 --- a/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileName#A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Rea_89a6d38e.MethodAsync.g.verified.cs +++ /dev/null @@ -1,13 +0,0 @@ -//HintName: A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Rea_89a6d38e.MethodAsync.g.cs -// -#nullable enable -namespace A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Reasonable; -public partial class OuterClass -{ - public partial class InnerClass - { - public void Method() - { - } - } -} diff --git a/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileNameKeepsOverloadsApart#A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.R_79dbcd19.MethodAsync_2.g.verified.cs b/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileNameKeepsOverloadsApart#A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.R_79dbcd19.MethodAsync_2.g.verified.cs deleted file mode 100644 index b81dba6..0000000 --- a/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileNameKeepsOverloadsApart#A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.R_79dbcd19.MethodAsync_2.g.verified.cs +++ /dev/null @@ -1,13 +0,0 @@ -//HintName: A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.R_79dbcd19.MethodAsync_2.g.cs -// -#nullable enable -namespace A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Reasonable; -public partial class OuterClass -{ - public partial class InnerClass - { - public void Method(int i) - { - } - } -} diff --git a/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileNameKeepsOverloadsApart#A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Rea_89a6d38e.MethodAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileNameKeepsOverloadsApart#A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Rea_89a6d38e.MethodAsync.g.verified.cs deleted file mode 100644 index 071af7c..0000000 --- a/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileNameKeepsOverloadsApart#A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Rea_89a6d38e.MethodAsync.g.verified.cs +++ /dev/null @@ -1,13 +0,0 @@ -//HintName: A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Rea_89a6d38e.MethodAsync.g.cs -// -#nullable enable -namespace A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Reasonable; -public partial class OuterClass -{ - public partial class InnerClass - { - public void Method() - { - } - } -} diff --git a/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileNameWhenTheMethodNameFillsIt#_b60220f2.ThisMethodHasAnUnreasonablyLongNameWhichOnItsOwnLeavesNoRoomForTheNamespaceOrTheConta.g.verified.cs b/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileNameWhenTheMethodNameFillsIt#_b60220f2.ThisMethodHasAnUnreasonablyLongNameWhichOnItsOwnLeavesNoRoomForTheNamespaceOrTheConta.g.verified.cs deleted file mode 100644 index 5f00229..0000000 --- a/tests/Generator.Tests/Snapshots/FileNameTests.ShortenLongFileNameWhenTheMethodNameFillsIt#_b60220f2.ThisMethodHasAnUnreasonablyLongNameWhichOnItsOwnLeavesNoRoomForTheNamespaceOrTheConta.g.verified.cs +++ /dev/null @@ -1,10 +0,0 @@ -//HintName: _b60220f2.ThisMethodHasAnUnreasonablyLongNameWhichOnItsOwnLeavesNoRoomForTheNamespaceOrTheConta.g.cs -// -#nullable enable -namespace A.Long.Enough.Namespace.To.Make.The.Generated.File.Name.Exceed.What.Is.Reasonable; -public partial class OuterClass -{ - public void ThisMethodHasAnUnreasonablyLongNameWhichOnItsOwnLeavesNoRoomForTheNamespaceOrTheContainingType() - { - } -} diff --git a/tests/Generator.Tests/Snapshots/TypeTests.ArrayParameter#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.ArrayParameter#g.verified.cs deleted file mode 100644 index 380f8e6..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.ArrayParameter#g.verified.cs +++ /dev/null @@ -1,4 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -public void Method(int[] o) -{ -} diff --git a/tests/Generator.Tests/Snapshots/TypeTests.BinaryPattern#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.BinaryPattern#g.verified.cs deleted file mode 100644 index 8fa63d4..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.BinaryPattern#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -_ = new object() is global::System.DBNull or global::System.IO.Stream; diff --git a/tests/Generator.Tests/Snapshots/TypeTests.DeclarationExpression#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.DeclarationExpression#g.verified.cs deleted file mode 100644 index c13d1ed..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.DeclarationExpression#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -new global::System.Collections.Generic.Dictionary().TryGetValue(0, out global::System.IO.Stream a); diff --git a/tests/Generator.Tests/Snapshots/TypeTests.EnumPatternName#.Class.ReturnTrueAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.EnumPatternName#Class.ReturnTrueAsync.g.verified.cs similarity index 78% rename from tests/Generator.Tests/Snapshots/TypeTests.EnumPatternName#.Class.ReturnTrueAsync.g.verified.cs rename to tests/Generator.Tests/Snapshots/TypeTests.EnumPatternName#Class.ReturnTrueAsync.g.verified.cs index d64773f..b3fd43e 100644 --- a/tests/Generator.Tests/Snapshots/TypeTests.EnumPatternName#.Class.ReturnTrueAsync.g.verified.cs +++ b/tests/Generator.Tests/Snapshots/TypeTests.EnumPatternName#Class.ReturnTrueAsync.g.verified.cs @@ -1,4 +1,4 @@ -//HintName: .Class.ReturnTrueAsync.g.cs +//HintName: Class.ReturnTrueAsync.g.cs // #nullable enable partial class Class diff --git a/tests/Generator.Tests/Snapshots/TypeTests.FullyQualifiedArray#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.FullyQualifiedArray#g.verified.cs deleted file mode 100644 index 846696c..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.FullyQualifiedArray#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -global::System.Text.RegularExpressions.Regex[] variable = null!; diff --git a/tests/Generator.Tests/Snapshots/TypeTests.GenericClassWithGenericInnerClass#Test.Class{T}.FooAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.GenericClassWithGenericInnerClass#Test.Class{T}.FooAsync.g.verified.cs deleted file mode 100644 index cc511f8..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.GenericClassWithGenericInnerClass#Test.Class{T}.FooAsync.g.verified.cs +++ /dev/null @@ -1,8 +0,0 @@ -//HintName: Test.Class{T}.FooAsync.g.cs -// -#nullable enable -namespace Test; -internal partial class Class -{ - public void Foo(global::Test.Class.Int i) { } -} diff --git a/tests/Generator.Tests/Snapshots/TypeTests.NestedGenerics#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.NestedGenerics#g.verified.cs deleted file mode 100644 index f9df188..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.NestedGenerics#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -var dict = new global::System.Collections.Generic.Dictionary>(); diff --git a/tests/Generator.Tests/Snapshots/TypeTests.NotPattern#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.NotPattern#g.verified.cs deleted file mode 100644 index fe1035f..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.NotPattern#g.verified.cs +++ /dev/null @@ -1,3 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -_ = new object() is not global::System.DBNull; - diff --git a/tests/Generator.Tests/Snapshots/TypeTests.NullableDeclarationExpression#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.NullableDeclarationExpression#g.verified.cs deleted file mode 100644 index c7ab4f3..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.NullableDeclarationExpression#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -new global::System.Collections.Generic.Dictionary().TryGetValue(0, out global::System.IO.Stream? a); diff --git a/tests/Generator.Tests/Snapshots/TypeTests.NullableForeach#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.NullableForeach#g.verified.cs deleted file mode 100644 index c2a3105..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.NullableForeach#g.verified.cs +++ /dev/null @@ -1,4 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -foreach (global::System.IO.Stream? i in global::System.Array.Empty()) -{ -} diff --git a/tests/Generator.Tests/Snapshots/TypeTests.PatternIsEnumMember#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.PatternIsEnumMember#g.verified.cs deleted file mode 100644 index 31e8cad..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.PatternIsEnumMember#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -_ = global::System.StringComparison.CurrentCulture is global::System.StringComparison.CurrentCulture; diff --git a/tests/Generator.Tests/Snapshots/TypeTests.PatternIsNotLiteral#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.PatternIsNotLiteral#g.verified.cs deleted file mode 100644 index 5cfd56a..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.PatternIsNotLiteral#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -_ = global::System.StringComparison.CurrentCulture is not global::System.StringComparison.CurrentCulture; diff --git a/tests/Generator.Tests/Snapshots/TypeTests.PatternMatchingWithConstant#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.PatternMatchingWithConstant#g.verified.cs deleted file mode 100644 index e010dff..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.PatternMatchingWithConstant#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -_ = 1 is 2; diff --git a/tests/Generator.Tests/Snapshots/TypeTests.QualifiedGenericName#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.QualifiedGenericName#g.verified.cs deleted file mode 100644 index dbd8c8b..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.QualifiedGenericName#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -global::System.Collections.Generic.HashSet z = null!; diff --git a/tests/Generator.Tests/Snapshots/TypeTests.QualifiedNonGenericName#System.Class.MethodAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.QualifiedNonGenericName#System.Class.MethodAsync.g.verified.cs deleted file mode 100644 index 8aff483..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.QualifiedNonGenericName#System.Class.MethodAsync.g.verified.cs +++ /dev/null @@ -1,11 +0,0 @@ -//HintName: System.Class.MethodAsync.g.cs -// -#nullable enable -namespace System; -public partial class Class -{ - public void Method() - { - global::System.Security.Cryptography.CryptographicException z = null!; - } -} diff --git a/tests/Generator.Tests/Snapshots/TypeTests.VariableDeclaration#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.VariableDeclaration#g.verified.cs deleted file mode 100644 index d39a3c5..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.VariableDeclaration#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -global::System.IO.MemoryStream ms = new(); diff --git a/tests/Generator.Tests/Snapshots/TypeTests.VariableDeclarationRedundant#g.verified.cs b/tests/Generator.Tests/Snapshots/TypeTests.VariableDeclarationRedundant#g.verified.cs deleted file mode 100644 index 06b808a..0000000 --- a/tests/Generator.Tests/Snapshots/TypeTests.VariableDeclarationRedundant#g.verified.cs +++ /dev/null @@ -1,2 +0,0 @@ -//HintName: Test.Class.MethodAsync.g.cs -global::System.IO.MemoryStream ms = new global::System.IO.MemoryStream(); diff --git a/tests/Generator.Tests/Snapshots/UnitTests.MultipleNamespaces#NsOne.NsTwo.NsThree.NsFour.MultipleNamespaces.EmptyAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/UnitTests.MultipleNamespaces#NsOne.NsTwo.NsThree.NsFour.MultipleNamespaces.EmptyAsync.g.verified.cs deleted file mode 100644 index 8176c1a..0000000 --- a/tests/Generator.Tests/Snapshots/UnitTests.MultipleNamespaces#NsOne.NsTwo.NsThree.NsFour.MultipleNamespaces.EmptyAsync.g.verified.cs +++ /dev/null @@ -1,18 +0,0 @@ -//HintName: NsOne.NsTwo.NsThree.NsFour.MultipleNamespaces.EmptyAsync.g.cs -// -#nullable enable -namespace NsOne -{ - namespace NsTwo.NsThree - { - namespace NsFour - { - public partial class MultipleNamespaces - { - void Empty() - { - } - } - } - } -} diff --git a/tests/Generator.Tests/Snapshots/UnitTests.StaticUsings#N1.C1.MethodAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/UnitTests.StaticUsings#N1.C1.MethodAsync.g.verified.cs deleted file mode 100644 index a1f40f5..0000000 --- a/tests/Generator.Tests/Snapshots/UnitTests.StaticUsings#N1.C1.MethodAsync.g.verified.cs +++ /dev/null @@ -1,16 +0,0 @@ -//HintName: N1.C1.MethodAsync.g.cs -// -#nullable enable - -using static N2.C2; - -namespace N1 -{ - public partial class C1 - { - public void Method() - { - _ = global::N2.C2.OtherConst; - } - } -} diff --git a/tests/Generator.Tests/TypeTests.cs b/tests/Generator.Tests/TypeTests.cs index d4268e8..de27532 100644 --- a/tests/Generator.Tests/TypeTests.cs +++ b/tests/Generator.Tests/TypeTests.cs @@ -13,41 +13,6 @@ public Task ConvertExceptionType() => $$""" } """.Verify(false, true, sourceType: SourceType.MethodBody); - [Fact] - public Task NotPattern() => -"_ = new object() is not DBNull;" -.Verify(false, true, sourceType: SourceType.MethodBody); - - [Fact] - public Task PatternMatchingWithConstant() => -"_ = 1 is 2;" -.Verify(sourceType: SourceType.MethodBody); - - [Fact] - public Task PatternIsNotLiteral() => -"_ = StringComparison.CurrentCulture is not StringComparison.CurrentCulture;" -.Verify(sourceType: SourceType.MethodBody); - - [Fact] - public Task PatternIsEnumMember() => -"_ = StringComparison.CurrentCulture is StringComparison.CurrentCulture;" -.Verify(sourceType: SourceType.MethodBody); - - [Fact] - public Task VariableDeclarationRedundant() => -"MemoryStream ms = new MemoryStream();" -.Verify(false, true, sourceType: SourceType.MethodBody); - - [Fact] - public Task VariableDeclaration() => -"MemoryStream ms = new();" -.Verify(false, true, sourceType: SourceType.MethodBody); - - [Fact] - public Task BinaryPattern() => -"_ = new object() is DBNull or Stream;" -.Verify(false, true, sourceType: SourceType.MethodBody); - [Theory] [InlineData(true)] [InlineData(false)] @@ -84,16 +49,6 @@ public Task ReturnTrueAsync() """ .Verify(false, true, sourceType: SourceType.Full); - [Fact] - public Task DeclarationExpression() => -"new Dictionary().TryGetValue(0, out Stream a);" -.Verify(false, true, sourceType: SourceType.MethodBody); - - [Fact] - public Task NullableDeclarationExpression() => -"new Dictionary().TryGetValue(0, out Stream? a);" -.Verify(false, true, sourceType: SourceType.MethodBody); - [Fact] public Task ConvertForeachType() => $$""" foreach (Int32 i in new Int32[] { 1 }) @@ -103,20 +58,6 @@ public Task ConvertForeachType() => $$""" await Task.CompletedTask; """.Verify(false, true, sourceType: SourceType.MethodBody); - [Fact] - public Task NullableForeach() - => """ -foreach (Stream? i in Array.Empty()) -{ -} -""" - .Verify(sourceType: SourceType.MethodBody); - - [Fact] - public Task NestedGenerics() - => "var dict = new Dictionary>();" - .Verify(sourceType: SourceType.MethodBody); - [Fact] public Task TestTypes() => $$""" String myStr; @@ -129,14 +70,6 @@ public Task TestTypes() => $$""" await Task.CompletedTask; """.Verify(false, true, sourceType: SourceType.MethodBody); - [Fact] - public Task ArrayParameter() => """ -[CreateSyncVersion] -public async Task MethodAsync(Int32[] o) -{ -} -""".Verify(); - [Fact] public Task TwoDArrayParameter() => """ [CreateSyncVersion] @@ -156,11 +89,6 @@ public async Task GetCustomObjectAsync(object o) } """.Verify(); - [Fact] - public Task FullyQualifiedArray() - => "System.Text.RegularExpressions.Regex[] variable = null!;" - .Verify(sourceType: SourceType.MethodBody); - [Fact] public Task CastFullyQualifiedTypeTwice() => """ class CustomClass { } @@ -203,29 +131,6 @@ public Task HandleNameOf() => "_ = nameof(Stream);" .Verify(sourceType: SourceType.MethodBody); - [Fact] - public Task QualifiedGenericName() - => "System.Collections.Generic.HashSet z = null!;" - .Verify(sourceType: SourceType.MethodBody); - - [Theory] - [InlineData("")] - [InlineData("System.")] - [InlineData("global::System.")] - public Task QualifiedNonGenericName(string prefix) - => $$""" -namespace System; - -public partial class Class -{ - [CreateSyncVersion] - public async Task MethodAsync() - { - {{prefix}}Security.Cryptography.CryptographicException z = null!; - } -} -""".Verify(disableUnique: true, sourceType: SourceType.Full); - [Fact] public Task HandleAsCast() => "_ = new object() as Stream;" @@ -249,19 +154,6 @@ public async Task SwitchAsync(Stream stream) } """.Verify(); - [Fact] - public Task GenericClassWithGenericInnerClass() => """ -namespace Test; - -internal partial class Class -{ - [CreateSyncVersion] - public async Task FooAsync(Int i) { } - - internal class Int { } -} -""".Verify(sourceType: SourceType.Full); - [Fact] public Task EventHandlerType() => "_ = new DataReceivedEventHandler((s, e) => { });" diff --git a/tests/Generator.Tests/UnitTests.cs b/tests/Generator.Tests/UnitTests.cs index 80e110f..a355337 100644 --- a/tests/Generator.Tests/UnitTests.cs +++ b/tests/Generator.Tests/UnitTests.cs @@ -74,52 +74,6 @@ public async Task FooAsync() public T Bar(Func innerLogic) => innerLogic(); """.Verify(); - [Fact] - public Task MultipleNamespaces() => """ -namespace NsOne -{ - namespace NsTwo.NsThree - { - namespace NsFour - { - public partial class MultipleNamespaces - { - [CreateSyncVersion] - async void EmptyAsync() - { - } - } - } - } -} -""".Verify(sourceType: SourceType.Full); - - [Fact] - public Task StaticUsings() => """ -using static N2.C2; - -namespace N1 -{ - public partial class C1 - { - [CreateSyncVersion] - public async Task MethodAsync() - { - _ = OtherConst; - } - } -} - -namespace N2 -{ - public class C2 - { - public const int OtherConst = 1; - } -} - -""".Verify(sourceType: SourceType.Full); - [Fact] public Task GenericClass() => """ namespace Test;