From f53ce835227d667bb3bfd1571a967c3e7e6ccfb6 Mon Sep 17 00:00:00 2001 From: costin-zaharia-sonarsource Date: Fri, 9 Aug 2024 13:34:22 +0200 Subject: [PATCH] ShimLayer: Synchonize OperationLightupGenerator.cs with StyleCop --- .../OperationLightupGenerator.cs | 464 +++++++++++------- 1 file changed, 288 insertions(+), 176 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.ShimLayer.CodeGeneration/OperationLightupGenerator.cs b/analyzers/src/SonarAnalyzer.ShimLayer.CodeGeneration/OperationLightupGenerator.cs index 53f20e8a143..e61b1ade864 100644 --- a/analyzers/src/SonarAnalyzer.ShimLayer.CodeGeneration/OperationLightupGenerator.cs +++ b/analyzers/src/SonarAnalyzer.ShimLayer.CodeGeneration/OperationLightupGenerator.cs @@ -3,26 +3,31 @@ namespace StyleCop.Analyzers.CodeGeneration { + using System; + using System.Collections.Generic; + using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Globalization; using System.IO; + using System.Linq; using System.Text; using System.Xml.Linq; using System.Xml.XPath; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Text; [Generator] - internal sealed class OperationLightupGenerator : ISourceGenerator + internal sealed class OperationLightupGenerator : IIncrementalGenerator { - public void Initialize(GeneratorInitializationContext context) + public void Initialize(IncrementalGeneratorInitializationContext context) { + var operationInterfacesFiles = context.AdditionalTextsProvider.Where(static x => Path.GetFileName(x.Path) == "OperationInterfaces.xml"); + context.RegisterSourceOutput(operationInterfacesFiles, this.Execute); } - public void Execute(GeneratorExecutionContext context) + private void Execute(SourceProductionContext context, AdditionalText operationInterfacesFile) { - var operationInterfacesFile = context.AdditionalFiles.Single(x => Path.GetFileName(x.Path) == "OperationInterfaces.xml"); var operationInterfacesText = operationInterfacesFile.GetText(context.CancellationToken); if (operationInterfacesText is null) { @@ -33,7 +38,7 @@ public void Execute(GeneratorExecutionContext context) this.GenerateOperationInterfaces(in context, operationInterfaces); } - private void GenerateOperationInterfaces(in GeneratorExecutionContext context, XDocument operationInterfaces) + private void GenerateOperationInterfaces(in SourceProductionContext context, XDocument operationInterfaces) { var tree = operationInterfaces.XPathSelectElement("/Tree"); if (tree is null) @@ -51,32 +56,35 @@ private void GenerateOperationInterfaces(in GeneratorExecutionContext context, X this.GenerateOperationKindEx(in context, documentData.Interfaces.Values.ToImmutableArray()); } - private void GenerateOperationInterface(in GeneratorExecutionContext context, InterfaceData node) + private void GenerateOperationInterface(in SourceProductionContext context, InterfaceData node) { var members = SyntaxFactory.List(); // internal const string WrappedTypeName = "Microsoft.CodeAnalysis.Operations.IArgumentOperation"; members = members.Add(SyntaxFactory.FieldDeclaration( attributeLists: default, - modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.ConstKeyword)), + modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.ConstKeyword)), // Sonar declaration: SyntaxFactory.VariableDeclaration( type: SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)), variables: SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator( identifier: SyntaxFactory.Identifier("WrappedTypeName"), argumentList: null, - initializer: SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"Microsoft.CodeAnalysis.{node.Namespace}.{node.InterfaceName}")))))))); + initializer: SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"{node.Namespace}.{node.InterfaceName}")))))))); - // private static readonly Type WrappedType; - members = members.Add(SyntaxFactory.FieldDeclaration( - attributeLists: default, - modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)), - declaration: SyntaxFactory.VariableDeclaration( - type: SyntaxFactory.IdentifierName("Type"), - variables: SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator("WrappedType"))))); + if (node.InterfaceName != "IOperation") + { + // private static readonly Type WrappedType; + members = members.Add(SyntaxFactory.FieldDeclaration( + attributeLists: default, + modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)), + declaration: SyntaxFactory.VariableDeclaration( + type: SyntaxFactory.IdentifierName("Type"), + variables: SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator("WrappedType"))))); + } foreach (var property in node.Properties) { - if (property.IsSkipped) + if (property.IsSkipped || !property.NeedsAccessor) { continue; } @@ -105,21 +113,26 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In type: SyntaxFactory.IdentifierName("IOperation"), variables: SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator("operation"))))); - var staticCtorStatements = SyntaxFactory.SingletonList( - SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, - left: SyntaxFactory.IdentifierName("WrappedType"), - right: SyntaxFactory.InvocationExpression( - expression: SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - expression: SyntaxFactory.IdentifierName("OperationWrapperHelper"), - name: SyntaxFactory.IdentifierName("GetWrappedType")), - argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument( - SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName(node.WrapperName))))))))); + var staticCtorStatements = SyntaxFactory.List(); + + if (node.InterfaceName != "IOperation") + { + staticCtorStatements = staticCtorStatements.Add( + SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + left: SyntaxFactory.IdentifierName("WrappedType"), + right: SyntaxFactory.InvocationExpression( + expression: SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + expression: SyntaxFactory.IdentifierName("OperationWrapperHelper"), + name: SyntaxFactory.IdentifierName("GetWrappedType")), + argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument( + SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName(node.WrapperName))))))))); + } foreach (var property in node.Properties) { - if (property.IsSkipped) + if (property.IsSkipped || !property.NeedsAccessor) { continue; } @@ -146,6 +159,9 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In } // ConstructorAccessor = LightupHelpers.CreateOperationPropertyAccessor(WrappedType, nameof(Constructor)); + ExpressionSyntax wrappedType = node.InterfaceName == "IOperation" + ? SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName("IOperation")) + : SyntaxFactory.IdentifierName("WrappedType"); staticCtorStatements = staticCtorStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, left: SyntaxFactory.IdentifierName(property.AccessorName), @@ -157,7 +173,7 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( new[] { - SyntaxFactory.Argument(SyntaxFactory.IdentifierName("WrappedType")), + SyntaxFactory.Argument(wrappedType), SyntaxFactory.Argument(SyntaxFactory.InvocationExpression( expression: SyntaxFactory.IdentifierName("nameof"), argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(property.Name)))))), @@ -217,30 +233,33 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In initializer: null, semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken))); - // public ITypeSymbol Type => this.WrappedOperation.Type; - members = members.Add(SyntaxFactory.PropertyDeclaration( - attributeLists: default, - modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)), - type: SyntaxFactory.IdentifierName("ITypeSymbol"), - explicitInterfaceSpecifier: null, - identifier: SyntaxFactory.Identifier("Type"), - accessorList: null, - expressionBody: SyntaxFactory.ArrowExpressionClause(SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - expression: SyntaxFactory.MemberAccessExpression( + if (node.InterfaceName != "IOperation") + { + // public ITypeSymbol Type => this.WrappedOperation.Type; + members = members.Add(SyntaxFactory.PropertyDeclaration( + attributeLists: default, + modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)), + type: SyntaxFactory.IdentifierName("ITypeSymbol"), + explicitInterfaceSpecifier: null, + identifier: SyntaxFactory.Identifier("Type"), + accessorList: null, + expressionBody: SyntaxFactory.ArrowExpressionClause(SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - expression: SyntaxFactory.ThisExpression(), - name: SyntaxFactory.IdentifierName("WrappedOperation")), - name: SyntaxFactory.IdentifierName("Type"))), - initializer: null, - semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken))); + expression: SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + expression: SyntaxFactory.ThisExpression(), + name: SyntaxFactory.IdentifierName("WrappedOperation")), + name: SyntaxFactory.IdentifierName("Type"))), + initializer: null, + semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken))); + } foreach (var property in node.Properties) { if (property.IsSkipped) { // Generate a NotImplementedException for public properties that do not have a supported type - if (property.IsPublicProperty && !property.IsOverride) + if (property.IsPublicProperty && !property.IsOverride) // Sonar { // public object Constructor => throw new NotImplementedException("Property 'Type.Property' has unsupported type 'Type'"); members = members.Add(SyntaxFactory.PropertyDeclaration( @@ -266,14 +285,30 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In var propertyType = property.NeedsWrapper ? SyntaxFactory.IdentifierName(property.WrappedType) : property.AccessorResultType; // Sonar - // ConstructorAccessor(this.WrappedOperation) - var evaluatedAccessor = SyntaxFactory.InvocationExpression( - expression: SyntaxFactory.IdentifierName(property.AccessorName), - argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument( + // The value is accessed in one of the following ways: + // ConstructorAccessor(this.WrappedOperation) + // this.WrappedOperation.Constructor + ExpressionSyntax evaluatedAccessor; + if (property.NeedsAccessor) + { + evaluatedAccessor = SyntaxFactory.InvocationExpression( + expression: SyntaxFactory.IdentifierName(property.AccessorName), + argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument( + expression: SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + expression: SyntaxFactory.ThisExpression(), + name: SyntaxFactory.IdentifierName("WrappedOperation")))))); + } + else + { + evaluatedAccessor = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, expression: SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, expression: SyntaxFactory.ThisExpression(), - name: SyntaxFactory.IdentifierName("WrappedOperation")))))); + name: SyntaxFactory.IdentifierName("WrappedOperation")), + name: SyntaxFactory.IdentifierName(property.Name)); + } ExpressionSyntax convertedResult; if (property.NeedsWrapper) @@ -304,9 +339,10 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken))); } - if (node.BaseInterface is { } baseDefinition) + foreach (var baseDefinition in node.InheritedInterfaces) { - var inheritedProperties = baseDefinition.Properties; + // For now, don't inherit properties from IOperationWrapper + var inheritedProperties = baseDefinition.InterfaceName != "IOperation" ? baseDefinition.Properties : ImmutableArray.Empty; foreach (var property in inheritedProperties) { if (node.Properties.Any(derivedProperty => derivedProperty.Name == property.Name && derivedProperty.IsNew)) @@ -407,6 +443,68 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In // // return new IArgumentOperationWrapper(operation); // } + var fromOperationStatements = new List(); + fromOperationStatements.Add(SyntaxFactory.IfStatement( + condition: SyntaxFactory.BinaryExpression( + SyntaxKind.EqualsExpression, + left: SyntaxFactory.IdentifierName("operation"), + right: SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)), + statement: SyntaxFactory.Block( + SyntaxFactory.ReturnStatement(SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression))))); + + if (node.InterfaceName != "IOperation") + { + fromOperationStatements.Add(SyntaxFactory.IfStatement( + condition: SyntaxFactory.PrefixUnaryExpression( + SyntaxKind.LogicalNotExpression, + operand: SyntaxFactory.InvocationExpression( + expression: SyntaxFactory.IdentifierName("IsInstance"), + argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.IdentifierName("operation")))))), + statement: SyntaxFactory.Block( + SyntaxFactory.ThrowStatement(SyntaxFactory.ObjectCreationExpression( + type: SyntaxFactory.IdentifierName("InvalidCastException"), + argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument( + SyntaxFactory.InterpolatedStringExpression( + SyntaxFactory.Token(SyntaxKind.InterpolatedStringStartToken), + SyntaxFactory.List(new InterpolatedStringContentSyntax[] + { + SyntaxFactory.InterpolatedStringText(SyntaxFactory.Token( + leading: default, + SyntaxKind.InterpolatedStringTextToken, + "Cannot cast '", + "Cannot cast '", + trailing: default)), + SyntaxFactory.Interpolation(SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + expression: SyntaxFactory.InvocationExpression( + expression: SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + expression: SyntaxFactory.IdentifierName("operation"), + name: SyntaxFactory.IdentifierName("GetType")), + argumentList: SyntaxFactory.ArgumentList()), + name: SyntaxFactory.IdentifierName("FullName"))), + SyntaxFactory.InterpolatedStringText(SyntaxFactory.Token( + leading: default, + SyntaxKind.InterpolatedStringTextToken, + "' to '", + "' to '", + trailing: default)), + SyntaxFactory.Interpolation(SyntaxFactory.IdentifierName("WrappedTypeName")), + SyntaxFactory.InterpolatedStringText(SyntaxFactory.Token( + leading: default, + SyntaxKind.InterpolatedStringTextToken, + "'", + "'", + trailing: default)), + }))))), + initializer: null))))); + } + + fromOperationStatements.Add(SyntaxFactory.ReturnStatement(SyntaxFactory.ObjectCreationExpression( + type: SyntaxFactory.IdentifierName(node.WrapperName), + argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.IdentifierName("operation")))), + initializer: null))); + members = members.Add(SyntaxFactory.MethodDeclaration( attributeLists: default, modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)), @@ -421,68 +519,35 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In identifier: SyntaxFactory.Identifier("operation"), @default: null))), constraintClauses: default, - body: SyntaxFactory.Block( - SyntaxFactory.IfStatement( - condition: SyntaxFactory.BinaryExpression( - SyntaxKind.EqualsExpression, - left: SyntaxFactory.IdentifierName("operation"), - right: SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)), - statement: SyntaxFactory.Block( - SyntaxFactory.ReturnStatement(SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression)))), - SyntaxFactory.IfStatement( - condition: SyntaxFactory.PrefixUnaryExpression( - SyntaxKind.LogicalNotExpression, - operand: SyntaxFactory.InvocationExpression( - expression: SyntaxFactory.IdentifierName("IsInstance"), - argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.IdentifierName("operation")))))), - statement: SyntaxFactory.Block( - SyntaxFactory.ThrowStatement(SyntaxFactory.ObjectCreationExpression( - type: SyntaxFactory.IdentifierName("InvalidCastException"), - argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument( - SyntaxFactory.InterpolatedStringExpression( - SyntaxFactory.Token(SyntaxKind.InterpolatedStringStartToken), - SyntaxFactory.List(new InterpolatedStringContentSyntax[] - { - SyntaxFactory.InterpolatedStringText(SyntaxFactory.Token( - leading: default, - SyntaxKind.InterpolatedStringTextToken, - "Cannot cast '", - "Cannot cast '", - trailing: default)), - SyntaxFactory.Interpolation(SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - expression: SyntaxFactory.InvocationExpression( - expression: SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - expression: SyntaxFactory.IdentifierName("operation"), - name: SyntaxFactory.IdentifierName("GetType")), - argumentList: SyntaxFactory.ArgumentList()), - name: SyntaxFactory.IdentifierName("FullName"))), - SyntaxFactory.InterpolatedStringText(SyntaxFactory.Token( - leading: default, - SyntaxKind.InterpolatedStringTextToken, - "' to '", - "' to '", - trailing: default)), - SyntaxFactory.Interpolation(SyntaxFactory.IdentifierName("WrappedTypeName")), - SyntaxFactory.InterpolatedStringText(SyntaxFactory.Token( - leading: default, - SyntaxKind.InterpolatedStringTextToken, - "'", - "'", - trailing: default)), - }))))), - initializer: null)))), - SyntaxFactory.ReturnStatement(SyntaxFactory.ObjectCreationExpression( - type: SyntaxFactory.IdentifierName(node.WrapperName), - argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.IdentifierName("operation")))), - initializer: null))), + body: SyntaxFactory.Block(fromOperationStatements), expressionBody: null)); // public static bool IsInstance(IOperation operation) // { // return operation != null && LightupHelpers.CanWrapOperation(operation, WrappedType); // } + ExpressionSyntax isInstanceExpression = SyntaxFactory.BinaryExpression( + SyntaxKind.NotEqualsExpression, + left: SyntaxFactory.IdentifierName("operation"), + right: SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)); + if (node.InterfaceName != "IOperation") + { + isInstanceExpression = SyntaxFactory.BinaryExpression( + SyntaxKind.LogicalAndExpression, + left: isInstanceExpression, + right: SyntaxFactory.InvocationExpression( + expression: SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + expression: SyntaxFactory.IdentifierName("LightupHelpers"), + name: SyntaxFactory.IdentifierName("CanWrapOperation")), + argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( + new[] + { + SyntaxFactory.Argument(SyntaxFactory.IdentifierName("operation")), + SyntaxFactory.Argument(SyntaxFactory.IdentifierName("WrappedType")), + })))); + } + members = members.Add(SyntaxFactory.MethodDeclaration( attributeLists: default, modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)), @@ -497,24 +562,7 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In identifier: SyntaxFactory.Identifier("operation"), @default: null))), constraintClauses: default, - body: SyntaxFactory.Block( - SyntaxFactory.ReturnStatement(SyntaxFactory.BinaryExpression( - SyntaxKind.LogicalAndExpression, - left: SyntaxFactory.BinaryExpression( - SyntaxKind.NotEqualsExpression, - left: SyntaxFactory.IdentifierName("operation"), - right: SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)), - right: SyntaxFactory.InvocationExpression( - expression: SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - expression: SyntaxFactory.IdentifierName("LightupHelpers"), - name: SyntaxFactory.IdentifierName("CanWrapOperation")), - argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( - new[] - { - SyntaxFactory.Argument(SyntaxFactory.IdentifierName("operation")), - SyntaxFactory.Argument(SyntaxFactory.IdentifierName("WrappedType")), - })))))), + body: SyntaxFactory.Block(SyntaxFactory.ReturnStatement(isInstanceExpression)), expressionBody: null)); if (node.IsAbstract) @@ -525,7 +573,7 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In // } members = members.Add(SyntaxFactory.MethodDeclaration( attributeLists: default, - modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)), + modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)), // Sonar returnType: SyntaxFactory.IdentifierName(node.WrapperName), explicitInterfaceSpecifier: null, identifier: SyntaxFactory.Identifier("FromUpcast"), @@ -547,19 +595,27 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In var wrapperStruct = SyntaxFactory.StructDeclaration( attributeLists: default, - modifiers: SyntaxTokenList.Create(SyntaxFactory.Token(SyntaxKind.PublicKeyword)).Add(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)), + modifiers: SyntaxTokenList.Create(SyntaxFactory.Token(SyntaxKind.PublicKeyword)).Add(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)), // Sonar identifier: SyntaxFactory.Identifier(node.WrapperName), typeParameterList: null, - baseList: SyntaxFactory.BaseList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.SimpleBaseType(SyntaxFactory.IdentifierName("IOperationWrapper")))), + baseList: SyntaxFactory.BaseList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.SimpleBaseType(SyntaxFactory.IdentifierName("IOperationWrapper")))), // Sonar constraintClauses: default, members: members); + + var usingDirectives = new List(); + usingDirectives.Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System"))); + if (node.InterfaceName == "IOperation") + { + usingDirectives.Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Collections.Generic"))); + } + + usingDirectives.Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Collections.Immutable"))); + usingDirectives.Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Microsoft.CodeAnalysis"))); + var wrapperNamespace = SyntaxFactory.NamespaceDeclaration( name: SyntaxFactory.ParseName("StyleCop.Analyzers.Lightup"), externs: default, - usings: SyntaxFactory.List() - .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System"))) - .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Collections.Immutable"))) - .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Microsoft.CodeAnalysis"))), + usings: SyntaxFactory.List(usingDirectives), members: SyntaxFactory.SingletonList(wrapperStruct)); wrapperNamespace = wrapperNamespace @@ -573,10 +629,10 @@ private void GenerateOperationInterface(in GeneratorExecutionContext context, In .WithTrailingTrivia( SyntaxFactory.CarriageReturnLineFeed); - context.AddSource(node.WrapperName + ".g.cs", SourceText.From(wrapperNamespace.ToFullString(), Encoding.UTF8)); + context.AddSource(node.WrapperName + ".g.cs", wrapperNamespace.GetText(Encoding.UTF8)); } - private void GenerateOperationWrapperHelper(in GeneratorExecutionContext context, ImmutableArray wrapperTypes) + private void GenerateOperationWrapperHelper(in SourceProductionContext context, ImmutableArray wrapperTypes) { // private static readonly ImmutableDictionary WrappedTypes; var wrappedTypes = SyntaxFactory.FieldDeclaration( @@ -633,7 +689,31 @@ private void GenerateOperationWrapperHelper(in GeneratorExecutionContext context foreach (var node in wrapperTypes) { - // builder.Add(typeof(IArgumentOperationWrapper), codeAnalysisAssembly.GetType(IArgumentOperationWrapper.WrappedTypeName)); + // For the base IOperation node: + // builder.Add(typeof(IArgumentOperationWrapper), typeof(IOperation)); + // + // For all other nodes: + // builder.Add(typeof(IArgumentOperationWrapper), codeAnalysisAssembly.GetType(IArgumentOperationWrapper.WrappedTypeName)); + ArgumentSyntax typeArgument; + if (node.InterfaceName == "IOperation") + { + typeArgument = SyntaxFactory.Argument(SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName("IOperation"))); + } + else + { + typeArgument = SyntaxFactory.Argument( + SyntaxFactory.InvocationExpression( + expression: SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + expression: SyntaxFactory.IdentifierName("codeAnalysisAssembly"), + name: SyntaxFactory.IdentifierName("GetType")), + argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + expression: SyntaxFactory.IdentifierName(node.WrapperName), + name: SyntaxFactory.IdentifierName("WrappedTypeName"))))))); + } + staticCtorStatements = staticCtorStatements.Add(SyntaxFactory.ExpressionStatement( SyntaxFactory.InvocationExpression( expression: SyntaxFactory.MemberAccessExpression( @@ -645,17 +725,7 @@ private void GenerateOperationWrapperHelper(in GeneratorExecutionContext context new[] { SyntaxFactory.Argument(SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName(node.WrapperName))), - SyntaxFactory.Argument( - SyntaxFactory.InvocationExpression( - expression: SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - expression: SyntaxFactory.IdentifierName("codeAnalysisAssembly"), - name: SyntaxFactory.IdentifierName("GetType")), - argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument( - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - expression: SyntaxFactory.IdentifierName(node.WrapperName), - name: SyntaxFactory.IdentifierName("WrappedTypeName"))))))), + typeArgument, }))))); } @@ -695,7 +765,7 @@ private void GenerateOperationWrapperHelper(in GeneratorExecutionContext context // } var getWrappedType = SyntaxFactory.MethodDeclaration( attributeLists: default, - modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)), + modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)), // Sonar returnType: SyntaxFactory.IdentifierName("Type"), explicitInterfaceSpecifier: null, identifier: SyntaxFactory.Identifier("GetWrappedType"), @@ -734,24 +804,24 @@ private void GenerateOperationWrapperHelper(in GeneratorExecutionContext context SyntaxFactory.Trivia(SyntaxFactory.DocumentationComment( SyntaxFactory.XmlText(" "), SyntaxFactory.XmlSummaryElement( - SyntaxFactory.XmlNewLine(Environment.NewLine), + SyntaxFactory.XmlText(XmlSyntaxFactory.XmlCarriageReturnLineFeedWithContinuation), SyntaxFactory.XmlText(" Gets the type that is wrapped by the given wrapper."), - SyntaxFactory.XmlNewLine(Environment.NewLine), + SyntaxFactory.XmlText(XmlSyntaxFactory.XmlCarriageReturnLineFeedWithContinuation), SyntaxFactory.XmlText(" ")), - SyntaxFactory.XmlNewLine(Environment.NewLine), + SyntaxFactory.XmlText(XmlSyntaxFactory.XmlCarriageReturnLineFeedWithContinuation), SyntaxFactory.XmlText(" "), SyntaxFactory.XmlParamElement( "wrapperType", SyntaxFactory.XmlText("Type of the wrapper for which the wrapped type should be retrieved.")), - SyntaxFactory.XmlNewLine(Environment.NewLine), + SyntaxFactory.XmlText(XmlSyntaxFactory.XmlCarriageReturnLineFeedWithContinuation), SyntaxFactory.XmlText(" "), SyntaxFactory.XmlReturnsElement( SyntaxFactory.XmlText("The wrapped type, or null if there is no info.")), - SyntaxFactory.XmlNewLine(Environment.NewLine).WithoutTrailingTrivia())))); + SyntaxFactory.XmlText(XmlSyntaxFactory.XmlCarriageReturnLineFeedWithContinuation).WithoutTrailingTrivia())))); var wrapperHelperClass = SyntaxFactory.ClassDeclaration( attributeLists: default, - modifiers: SyntaxTokenList.Create(SyntaxFactory.Token(SyntaxKind.PublicKeyword)).Add(SyntaxFactory.Token(SyntaxKind.StaticKeyword)), + modifiers: SyntaxTokenList.Create(SyntaxFactory.Token(SyntaxKind.PublicKeyword)).Add(SyntaxFactory.Token(SyntaxKind.StaticKeyword)), // Sonar identifier: SyntaxFactory.Identifier("OperationWrapperHelper"), typeParameterList: null, baseList: null, @@ -781,10 +851,10 @@ private void GenerateOperationWrapperHelper(in GeneratorExecutionContext context .WithTrailingTrivia( SyntaxFactory.CarriageReturnLineFeed); - context.AddSource("OperationWrapperHelper.g.cs", SourceText.From(wrapperNamespace.ToFullString(), Encoding.UTF8)); + context.AddSource("OperationWrapperHelper.g.cs", wrapperNamespace.GetText(Encoding.UTF8)); } - private void GenerateOperationKindEx(in GeneratorExecutionContext context, ImmutableArray wrapperTypes) + private void GenerateOperationKindEx(in SourceProductionContext context, ImmutableArray wrapperTypes) { var operationKinds = wrapperTypes .SelectMany(type => type.OperationKinds) @@ -810,7 +880,7 @@ private void GenerateOperationKindEx(in GeneratorExecutionContext context, Immut var operationKindExClass = SyntaxFactory.ClassDeclaration( attributeLists: default, - modifiers: SyntaxTokenList.Create(SyntaxFactory.Token(SyntaxKind.PublicKeyword)).Add(SyntaxFactory.Token(SyntaxKind.StaticKeyword)), + modifiers: SyntaxTokenList.Create(SyntaxFactory.Token(SyntaxKind.PublicKeyword)).Add(SyntaxFactory.Token(SyntaxKind.StaticKeyword)), // Sonar identifier: SyntaxFactory.Identifier("OperationKindEx"), typeParameterList: null, baseList: null, @@ -834,7 +904,7 @@ private void GenerateOperationKindEx(in GeneratorExecutionContext context, Immut .WithTrailingTrivia( SyntaxFactory.CarriageReturnLineFeed); - context.AddSource("OperationKindEx.g.cs", SourceText.From(wrapperNamespace.ToFullString(), Encoding.UTF8)); + context.AddSource("OperationKindEx.g.cs", wrapperNamespace.GetText(Encoding.UTF8)); } private sealed class DocumentData @@ -851,7 +921,7 @@ public DocumentData(XDocument document) continue; } - if (!operationKinds.TryGetValue(node.Attribute("Name").Value, out var kinds)) + if (!operationKinds.TryGetValue(node.RequiredAttribute("Name").Value, out var kinds)) { kinds = ImmutableArray<(string name, int value, string? extraDescription)>.Empty; } @@ -867,7 +937,7 @@ public DocumentData(XDocument document) continue; } - if (!operationKinds.TryGetValue(node.Attribute("Name").Value, out var kinds)) + if (!operationKinds.TryGetValue(node.RequiredAttribute("Name").Value, out var kinds)) { kinds = ImmutableArray<(string name, int value, string? extraDescription)>.Empty; } @@ -917,11 +987,11 @@ public DocumentData(XDocument document) continue; } - int parsedValue = ParsePrefixHexValue(entry.Attribute("Value").Value); - nodeBuilder.Add((entry.Attribute("Name").Value, parsedValue, entry.Attribute("ExtraDescription")?.Value)); + int parsedValue = ParsePrefixHexValue(entry.RequiredAttribute("Value").Value); + nodeBuilder.Add((entry.RequiredAttribute("Name").Value, parsedValue, entry.Attribute("ExtraDescription")?.Value)); } - builder.Add(node.Attribute("Name").Value, nodeBuilder.ToImmutable()); + builder.Add(node.RequiredAttribute("Name").Value, nodeBuilder.ToImmutable()); continue; } } @@ -938,7 +1008,7 @@ public DocumentData(XDocument document) operationKind++; } - var nodeName = node.Attribute("Name").Value; + var nodeName = node.RequiredAttribute("Name").Value; var kindName = nodeName.Substring("I".Length, nodeName.Length - "I".Length - "Operation".Length); builder.Add(nodeName, ImmutableArray.Create((kindName, operationKind, (string?)null))); } @@ -951,12 +1021,12 @@ private static ImmutableHashSet GetSkippedOperationKinds(XDocument document var builder = ImmutableHashSet.CreateBuilder(); foreach (var skippedKind in document.XPathSelectElements("/Tree/UnusedOperationKinds/Entry")) { - builder.Add(ParsePrefixHexValue(skippedKind.Attribute("Value").Value)); + builder.Add(ParsePrefixHexValue(skippedKind.RequiredAttribute("Value").Value)); } foreach (var explicitKind in document.XPathSelectElements("/Tree/*/OperationKind/Entry")) { - builder.Add(ParsePrefixHexValue(explicitKind.Attribute("Value").Value)); + builder.Add(ParsePrefixHexValue(explicitKind.RequiredAttribute("Value").Value)); } return builder.ToImmutable(); @@ -982,11 +1052,27 @@ public InterfaceData(DocumentData documentData, XElement node, ImmutableArray<(s this.documentData = documentData; this.OperationKinds = operationKinds; - this.InterfaceName = node.Attribute("Name").Value; - this.Namespace = node.Attribute("Namespace")?.Value ?? "Operations"; + this.InterfaceName = node.RequiredAttribute("Name").Value; + + if (node.Attribute("Namespace") is { } namespaceNode) + { + if (namespaceNode.Value == string.Empty) + { + this.Namespace = "Microsoft.CodeAnalysis"; + } + else + { + this.Namespace = $"Microsoft.CodeAnalysis.{namespaceNode.Value}"; + } + } + else + { + this.Namespace = "Microsoft.CodeAnalysis.Operations"; + } + this.Name = this.InterfaceName.Substring("I".Length, this.InterfaceName.Length - "I".Length - "Operation".Length); this.WrapperName = this.InterfaceName + "Wrapper"; - this.BaseInterfaceName = node.Attribute("Base").Value; + this.BaseInterfaceName = node.Attribute("Base")?.Value; this.IsAbstract = node.Name == "AbstractNode"; this.Properties = node.XPathSelectElements("Property").Select(property => new PropertyData(property)).ToImmutableArray(); } @@ -1001,7 +1087,7 @@ public InterfaceData(DocumentData documentData, XElement node, ImmutableArray<(s public string WrapperName { get; } - public string BaseInterfaceName { get; } + public string? BaseInterfaceName { get; } public bool IsAbstract { get; } @@ -1011,7 +1097,8 @@ public InterfaceData? BaseInterface { get { - if (this.documentData.Interfaces.TryGetValue(this.BaseInterfaceName, out var baseInterface)) + if (this.BaseInterfaceName is not null + && this.documentData.Interfaces.TryGetValue(this.BaseInterfaceName, out var baseInterface)) { return baseInterface; } @@ -1019,15 +1106,30 @@ public InterfaceData? BaseInterface return null; } } + + public IEnumerable InheritedInterfaces + { + get + { + var inheritedInterfaces = new List(); + for (var baseDefinition = this.BaseInterface; baseDefinition is not null; baseDefinition = baseDefinition.BaseInterface) + { + inheritedInterfaces.Add(baseDefinition); + } + + inheritedInterfaces.Reverse(); + return inheritedInterfaces; + } + } } private sealed class PropertyData { public PropertyData(XElement node) { - this.Name = node.Attribute("Name").Value; + this.Name = node.RequiredAttribute("Name").Value; this.AccessorName = this.Name + "Accessor"; - this.Type = node.Attribute("Type").Value; + this.Type = node.RequiredAttribute("Type").Value; this.TypeNonNullable = Type.TrimEnd('?'); // Sonar - When comparing types as strings, the nullable suffix should be ignored. this.WrappedType = TypeNonNullable + "Wrapper"; // Sonar @@ -1047,9 +1149,17 @@ public PropertyData(XElement node) "InterpolatedStringArgumentPlaceholderKind" => true, // Sonar: Skipped because it's not available "LoopKind" => true, "PlaceholderKind" => true, - _ => !this.IsPublicProperty || this.IsOverride, + _ => !this.IsPublicProperty || this.IsOverride, // Sonar }; + this.NeedsAccessor = this.Name switch + { + nameof(IOperation.Kind) => false, + nameof(IOperation.Syntax) => false, + nameof(IOperation.Type) => false, + nameof(IOperation.ConstantValue) => false, + _ => true, + }; this.NeedsWrapper = IsAnyOperation(TypeNonNullable) && TypeNonNullable != "IOperation"; // Sonar this.IsDerivedOperationArray = IsAnyOperationArray(TypeNonNullable) && TypeNonNullable != "ImmutableArray"; // Sonar @@ -1083,6 +1193,8 @@ public PropertyData(XElement node) public string Type { get; } + public bool NeedsAccessor { get; } + public string TypeNonNullable { get; } // Sonar public string WrappedType { get; } // Sonar