diff --git a/src/Analyzers/CSharp/CodeFixes/AssignOutParameters/AbstractAssignOutParametersCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/AssignOutParameters/AbstractAssignOutParametersCodeFixProvider.cs index 4a8fa36e5c05f..68b7d5aed208f 100644 --- a/src/Analyzers/CSharp/CodeFixes/AssignOutParameters/AbstractAssignOutParametersCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/AssignOutParameters/AbstractAssignOutParametersCodeFixProvider.cs @@ -160,7 +160,7 @@ protected static ImmutableArray GenerateAssignmentStatements( { result.Add(generator.ExpressionStatement(generator.AssignmentStatement( generator.IdentifierName(parameter.Name), - ExpressionGenerator.GenerateExpression(generator, parameter.Type, value: null, canUseFieldReference: false)))); + ExpressionGenerator.GenerateExpression(parameter.Type, value: null, canUseFieldReference: false)))); } return result.ToImmutableAndFree(); diff --git a/src/Analyzers/CSharp/CodeFixes/UseLocalFunction/CSharpUseLocalFunctionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseLocalFunction/CSharpUseLocalFunctionCodeFixProvider.cs index 251cec7f52af6..4b0d32d68b08c 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseLocalFunction/CSharpUseLocalFunctionCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseLocalFunction/CSharpUseLocalFunctionCodeFixProvider.cs @@ -99,7 +99,7 @@ protected override async Task FixAllAsync( foreach (var (localDeclaration, anonymousFunction, references) in nodesFromDiagnostics.OrderByDescending(nodes => nodes.function.SpanStart)) { var delegateType = (INamedTypeSymbol)semanticModel.GetTypeInfo(anonymousFunction, cancellationToken).ConvertedType; - var parameterList = GenerateParameterList(editor.Generator, anonymousFunction, delegateType.DelegateInvokeMethod); + var parameterList = GenerateParameterList(anonymousFunction, delegateType.DelegateInvokeMethod); var makeStatic = MakeStatic(semanticModel, makeStaticIfPossible, localDeclaration, cancellationToken); var currentLocalDeclaration = currentRoot.GetCurrentNode(localDeclaration); @@ -237,17 +237,17 @@ private static LocalFunctionStatementSyntax CreateLocalFunctionStatement( } private static ParameterListSyntax GenerateParameterList( - SyntaxGenerator generator, AnonymousFunctionExpressionSyntax anonymousFunction, IMethodSymbol delegateMethod) + AnonymousFunctionExpressionSyntax anonymousFunction, IMethodSymbol delegateMethod) { var parameterList = TryGetOrCreateParameterList(anonymousFunction); var i = 0; return parameterList != null - ? parameterList.ReplaceNodes(parameterList.Parameters, (parameterNode, _) => PromoteParameter(generator, parameterNode, delegateMethod.Parameters.ElementAtOrDefault(i++))) + ? parameterList.ReplaceNodes(parameterList.Parameters, (parameterNode, _) => PromoteParameter(parameterNode, delegateMethod.Parameters.ElementAtOrDefault(i++))) : ParameterList([.. delegateMethod.Parameters.Select(parameter => - PromoteParameter(generator, Parameter(parameter.Name.ToIdentifierToken()), parameter))]); + PromoteParameter(Parameter(parameter.Name.ToIdentifierToken()), parameter))]); - static ParameterSyntax PromoteParameter(SyntaxGenerator generator, ParameterSyntax parameterNode, IParameterSymbol delegateParameter) + static ParameterSyntax PromoteParameter(ParameterSyntax parameterNode, IParameterSymbol delegateParameter) { // delegateParameter may be null, consider this case: Action x = (a, b) => { }; // we will still fall back to object @@ -259,7 +259,7 @@ static ParameterSyntax PromoteParameter(SyntaxGenerator generator, ParameterSynt if (delegateParameter?.HasExplicitDefaultValue == true) { - parameterNode = parameterNode.WithDefault(GetDefaultValue(generator, delegateParameter)); + parameterNode = parameterNode.WithDefault(GetDefaultValue(delegateParameter)); } return parameterNode; @@ -312,6 +312,6 @@ private static int TryDetermineParameterIndex(NameColonSyntax argumentNameColon, return method.Parameters.IndexOf(p => p.Name == name); } - private static EqualsValueClauseSyntax GetDefaultValue(SyntaxGenerator generator, IParameterSymbol parameter) - => EqualsValueClause(ExpressionGenerator.GenerateExpression(generator, parameter.Type, parameter.ExplicitDefaultValue, canUseFieldReference: true)); + private static EqualsValueClauseSyntax GetDefaultValue(IParameterSymbol parameter) + => EqualsValueClause(ExpressionGenerator.GenerateExpression(parameter.Type, parameter.ExplicitDefaultValue, canUseFieldReference: true)); } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineMethod/CSharpInlineMethodRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineMethod/CSharpInlineMethodRefactoringProvider.cs index 923c2d561d687..400a1438d45e9 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineMethod/CSharpInlineMethodRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineMethod/CSharpInlineMethodRefactoringProvider.cs @@ -60,7 +60,7 @@ protected override SyntaxNode GenerateTypeSyntax(ITypeSymbol symbol, bool allowV => symbol.GenerateTypeSyntax(allowVar); protected override ExpressionSyntax GenerateLiteralExpression(ITypeSymbol typeSymbol, object? value) - => ExpressionGenerator.GenerateExpression(CSharpSyntaxGenerator.Instance, typeSymbol, value, canUseFieldReference: true); + => ExpressionGenerator.GenerateExpression(typeSymbol, value, canUseFieldReference: true); protected override bool IsFieldDeclarationSyntax(SyntaxNode node) => node.IsKind(SyntaxKind.FieldDeclaration); diff --git a/src/Features/CSharp/Portable/IntroduceParameter/CSharpIntroduceParameterCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/IntroduceParameter/CSharpIntroduceParameterCodeRefactoringProvider.cs index b440dbca15afb..1b291cdfd5564 100644 --- a/src/Features/CSharp/Portable/IntroduceParameter/CSharpIntroduceParameterCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/IntroduceParameter/CSharpIntroduceParameterCodeRefactoringProvider.cs @@ -12,22 +12,19 @@ namespace Microsoft.CodeAnalysis.CSharp.IntroduceParameter; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.IntroduceParameter), Shared] -internal sealed partial class CSharpIntroduceParameterCodeRefactoringProvider : AbstractIntroduceParameterCodeRefactoringProvider< +[method: ImportingConstructor] +[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] +internal sealed partial class CSharpIntroduceParameterCodeRefactoringProvider() + : AbstractIntroduceParameterCodeRefactoringProvider< ExpressionSyntax, InvocationExpressionSyntax, ObjectCreationExpressionSyntax, IdentifierNameSyntax, ArgumentSyntax> { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpIntroduceParameterCodeRefactoringProvider() - { - } - protected override SyntaxNode GenerateExpressionFromOptionalParameter(IParameterSymbol parameterSymbol) { - return ExpressionGenerator.GenerateExpression(CSharpSyntaxGenerator.Instance, parameterSymbol.Type, parameterSymbol.ExplicitDefaultValue, canUseFieldReference: true); + return ExpressionGenerator.GenerateExpression(parameterSymbol.Type, parameterSymbol.ExplicitDefaultValue, canUseFieldReference: true); } protected override SyntaxNode? GetLocalDeclarationFromDeclarator(SyntaxNode variableDecl) diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 1021ab21d2849..cf2aa2ed0d0e9 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -3134,17 +3134,6 @@ private static StatementSyntax AsStatement(SyntaxNode node) public override SyntaxNode ExpressionStatement(SyntaxNode expression) => SyntaxFactory.ExpressionStatement((ExpressionSyntax)expression); - internal override SyntaxNode MemberAccessExpressionWorker(SyntaxNode? expression, SyntaxNode simpleName) - { - // can only be null in VB - Contract.ThrowIfNull(expression); - - return SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - ParenthesizeLeft((ExpressionSyntax)expression), - (SimpleNameSyntax)simpleName); - } - public override SyntaxNode ConditionalAccessExpression(SyntaxNode expression, SyntaxNode whenNotNull) => SyntaxGeneratorInternal.ConditionalAccessExpression(expression, whenNotNull); @@ -3155,27 +3144,6 @@ public override SyntaxNode ElementBindingExpression(IEnumerable argu => SyntaxFactory.ElementBindingExpression( SyntaxFactory.BracketedArgumentList([.. arguments.Cast()])); - /// - /// Parenthesize the left hand size of a member access, invocation or element access expression - /// - private static ExpressionSyntax ParenthesizeLeft(ExpressionSyntax expression) - { - if (expression is TypeSyntax || - expression.Kind() - is SyntaxKind.ThisExpression - or SyntaxKind.BaseExpression - or SyntaxKind.ParenthesizedExpression - or SyntaxKind.SimpleMemberAccessExpression - or SyntaxKind.InvocationExpression - or SyntaxKind.ElementAccessExpression - or SyntaxKind.MemberBindingExpression) - { - return expression; - } - - return (ExpressionSyntax)Parenthesize(expression); - } - private static SeparatedSyntaxList AsExpressionList(IEnumerable expressions) => [.. expressions.OfType()]; @@ -3212,52 +3180,14 @@ private static ArgumentSyntax AsArgument(SyntaxNode argOrExpression) => argOrExpression as ArgumentSyntax ?? SyntaxFactory.Argument((ExpressionSyntax)argOrExpression); public override SyntaxNode InvocationExpression(SyntaxNode expression, IEnumerable arguments) - => SyntaxFactory.InvocationExpression(ParenthesizeLeft((ExpressionSyntax)expression), CreateArgumentList(arguments)); + => SyntaxFactory.InvocationExpression(CSharpSyntaxGeneratorInternal.ParenthesizeLeft((ExpressionSyntax)expression), CreateArgumentList(arguments)); public override SyntaxNode ElementAccessExpression(SyntaxNode expression, IEnumerable arguments) - => SyntaxFactory.ElementAccessExpression(ParenthesizeLeft((ExpressionSyntax)expression), SyntaxFactory.BracketedArgumentList(CreateArguments(arguments))); + => SyntaxFactory.ElementAccessExpression(CSharpSyntaxGeneratorInternal.ParenthesizeLeft((ExpressionSyntax)expression), SyntaxFactory.BracketedArgumentList(CreateArguments(arguments))); internal override SyntaxToken NumericLiteralToken(string text, ulong value) => SyntaxFactory.Literal(text, value); - public override SyntaxNode DefaultExpression(SyntaxNode type) - => SyntaxFactory.DefaultExpression((TypeSyntax)type).WithAdditionalAnnotations(Simplifier.Annotation); - - public override SyntaxNode DefaultExpression(ITypeSymbol type) - { - // If it's just a reference type, then "null" is the default expression for it. Note: - // this counts for actual reference type, or a type parameter with a 'class' constraint. - // Also, if it's a nullable type, then we can use "null". - if (type.IsReferenceType || - type is IPointerTypeSymbol || - type.IsNullable()) - { - return SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression); - } - - switch (type.SpecialType) - { - case SpecialType.System_Boolean: - return SyntaxFactory.LiteralExpression(SyntaxKind.FalseLiteralExpression); - case SpecialType.System_SByte: - case SpecialType.System_Byte: - case SpecialType.System_Int16: - case SpecialType.System_UInt16: - case SpecialType.System_Int32: - case SpecialType.System_UInt32: - case SpecialType.System_Int64: - case SpecialType.System_UInt64: - case SpecialType.System_Decimal: - case SpecialType.System_Single: - case SpecialType.System_Double: - return SyntaxFactory.LiteralExpression( - SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal("0", 0)); - } - - // Default to a "default()" expression. - return DefaultExpression(type.GenerateTypeSyntax()); - } - private static SyntaxNode Parenthesize(SyntaxNode expression, bool includeElasticTrivia = true, bool addSimplifierAnnotation = true) => CSharpSyntaxGeneratorInternal.Parenthesize(expression, includeElasticTrivia, addSimplifierAnnotation); @@ -3270,17 +3200,11 @@ public override SyntaxNode TypeOfExpression(SyntaxNode type) public override SyntaxNode TryCastExpression(SyntaxNode expression, SyntaxNode type) => SyntaxFactory.BinaryExpression(SyntaxKind.AsExpression, (ExpressionSyntax)Parenthesize(expression), (TypeSyntax)type); - public override SyntaxNode CastExpression(SyntaxNode type, SyntaxNode expression) - => SyntaxFactory.CastExpression((TypeSyntax)type, (ExpressionSyntax)Parenthesize(expression)).WithAdditionalAnnotations(Simplifier.Annotation); - - public override SyntaxNode ConvertExpression(SyntaxNode type, SyntaxNode expression) - => SyntaxFactory.CastExpression((TypeSyntax)type, (ExpressionSyntax)Parenthesize(expression)).WithAdditionalAnnotations(Simplifier.Annotation); - public override SyntaxNode AssignmentStatement(SyntaxNode left, SyntaxNode right) => SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, (ExpressionSyntax)left, (ExpressionSyntax)Parenthesize(right)); private static SyntaxNode CreateBinaryExpression(SyntaxKind syntaxKind, SyntaxNode left, SyntaxNode right) - => SyntaxFactory.BinaryExpression(syntaxKind, (ExpressionSyntax)Parenthesize(left), (ExpressionSyntax)Parenthesize(right)); + => CSharpSyntaxGeneratorInternal.CreateBinaryExpression(syntaxKind, left, right); public override SyntaxNode ValueEqualsExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.EqualsExpression, left, right); @@ -3327,9 +3251,6 @@ public override SyntaxNode ModuloExpression(SyntaxNode left, SyntaxNode right) public override SyntaxNode BitwiseAndExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.BitwiseAndExpression, left, right); - public override SyntaxNode BitwiseOrExpression(SyntaxNode left, SyntaxNode right) - => CreateBinaryExpression(SyntaxKind.BitwiseOrExpression, left, right); - public override SyntaxNode BitwiseNotExpression(SyntaxNode operand) => SyntaxFactory.PrefixUnaryExpression(SyntaxKind.BitwiseNotExpression, (ExpressionSyntax)Parenthesize(operand)); @@ -3355,13 +3276,10 @@ public override SyntaxNode BaseExpression() => SyntaxFactory.BaseExpression(); public override SyntaxNode TypedConstantExpression(TypedConstant value) - => ExpressionGenerator.GenerateExpression(this, value); + => ExpressionGenerator.GenerateExpression(value); private protected override SyntaxNode GenerateExpression(ITypeSymbol? type, object? value, bool canUseFieldReference) - => ExpressionGenerator.GenerateExpression(this, type, value, canUseFieldReference); - - public override SyntaxNode IdentifierName(string identifier) - => identifier.ToIdentifierName(); + => ExpressionGenerator.GenerateExpression(type, value, canUseFieldReference); public override SyntaxNode GenericName(string identifier, IEnumerable typeArguments) => GenericName(identifier.ToIdentifierToken(), typeArguments); @@ -3411,17 +3329,6 @@ internal override SyntaxNode GlobalAliasedName(SyntaxNode name) public override SyntaxNode NameExpression(INamespaceOrTypeSymbol namespaceOrTypeSymbol) => namespaceOrTypeSymbol.GenerateNameSyntax(); - private protected override SyntaxNode TypeExpression(ITypeSymbol typeSymbol, RefKind refKind) - { - var type = typeSymbol.GenerateTypeSyntax(); - return refKind switch - { - RefKind.Ref => SyntaxFactory.RefType(type), - RefKind.RefReadOnly => SyntaxFactory.RefType(RefKeyword, ReadOnlyKeyword, type), - _ => type, - }; - } - public override SyntaxNode TypeExpression(SpecialType specialType) => SyntaxFactory.PredefinedType(specialType switch { diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index 78546cfe44f97..a6c528cdbcc8a 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -192,7 +192,7 @@ internal SyntaxNode MethodDeclaration(IMethodSymbol method, string name, IEnumer name, typeParameters: method.TypeParameters.Select(p => TypeParameter(p)), parameters: method.Parameters.Select(p => ParameterDeclaration(p)), - returnType: method.ReturnType.IsSystemVoid() ? null : TypeExpression(method.ReturnType, method.RefKind), + returnType: method.ReturnType.IsSystemVoid() ? null : this.SyntaxGeneratorInternal.TypeExpression(method.ReturnType, method.RefKind), accessibility: method.DeclaredAccessibility, modifiers: DeclarationModifiers.From(method), statements: statements); @@ -284,7 +284,7 @@ public SyntaxNode OperatorDeclaration(IMethodSymbol method, IEnumerable ParameterDeclaration(p)), - returnType: method.ReturnType.IsSystemVoid() ? null : TypeExpression(method.ReturnType, method.RefKind), + returnType: method.ReturnType.IsSystemVoid() ? null : this.SyntaxGeneratorInternal.TypeExpression(method.ReturnType, method.RefKind), accessibility: method.DeclaredAccessibility, modifiers: DeclarationModifiers.From(method), statements: statements); @@ -389,7 +389,7 @@ public SyntaxNode PropertyDeclaration( var propDecl = PropertyDeclaration( property.Name, - TypeExpression(property.Type, property.RefKind), + this.SyntaxGeneratorInternal.TypeExpression(property.Type, property.RefKind), getAccessor, setAccessor, propertyAccessibility, @@ -450,7 +450,7 @@ public SyntaxNode IndexerDeclaration( { var indexerDecl = IndexerDeclaration( indexer.Parameters.Select(p => this.ParameterDeclaration(p)), - TypeExpression(indexer.Type, indexer.RefKind), + this.SyntaxGeneratorInternal.TypeExpression(indexer.Type, indexer.RefKind), indexer.DeclaredAccessibility, DeclarationModifiers.From(indexer), getAccessorStatements, @@ -1775,8 +1775,11 @@ internal SyntaxNode InterpolationFormatClause(string format) /// An expression that represents the default value of a type. /// This is typically a null value for reference types or a zero-filled value for value types. /// - public abstract SyntaxNode DefaultExpression(SyntaxNode type); - public abstract SyntaxNode DefaultExpression(ITypeSymbol type); + public SyntaxNode DefaultExpression(SyntaxNode type) + => this.SyntaxGeneratorInternal.DefaultExpression(type); + + public SyntaxNode DefaultExpression(ITypeSymbol type) + => this.SyntaxGeneratorInternal.DefaultExpression(type); /// /// Creates an expression that denotes the containing method's this-parameter. @@ -1828,7 +1831,8 @@ public SyntaxNode NullLiteralExpression() /// /// /// - public abstract SyntaxNode IdentifierName(string identifier); + public SyntaxNode IdentifierName(string identifier) + => this.SyntaxGeneratorInternal.IdentifierName(identifier); internal abstract SyntaxNode IdentifierName(SyntaxToken identifier); internal SyntaxToken Identifier(string identifier) => SyntaxGeneratorInternal.Identifier(identifier); @@ -1927,9 +1931,7 @@ public SyntaxNode DottedName(string dottedName) /// Creates an expression that denotes a type. /// public SyntaxNode TypeExpression(ITypeSymbol typeSymbol) - => TypeExpression(typeSymbol, RefKind.None); - - private protected abstract SyntaxNode TypeExpression(ITypeSymbol typeSymbol, RefKind refKind); + => this.SyntaxGeneratorInternal.TypeExpression(typeSymbol); /// /// Creates an expression that denotes a type. If addImport is false, @@ -2104,7 +2106,8 @@ public SyntaxNode TupleElementExpression(ITypeSymbol type, string? name = null) /// /// Creates an expression that denotes a bitwise-or operation. /// - public abstract SyntaxNode BitwiseOrExpression(SyntaxNode left, SyntaxNode right); + public SyntaxNode BitwiseOrExpression(SyntaxNode left, SyntaxNode right) + => this.SyntaxGeneratorInternal.BitwiseOrExpression(left, right); /// /// Creates an expression that denotes a bitwise-not operation @@ -2163,13 +2166,11 @@ public SyntaxNode ElementBindingExpression(params SyntaxNode[] arguments) /// /// Creates a member access expression. /// - public virtual SyntaxNode MemberAccessExpression(SyntaxNode? expression, SyntaxNode memberName) - { - return MemberAccessExpressionWorker(expression, memberName) - .WithAdditionalAnnotations(Simplifier.Annotation); - } + public SyntaxNode MemberAccessExpression(SyntaxNode? expression, SyntaxNode memberName) + => this.SyntaxGeneratorInternal.MemberAccessExpression(expression, memberName); - internal abstract SyntaxNode MemberAccessExpressionWorker(SyntaxNode? expression, SyntaxNode memberName); + internal SyntaxNode MemberAccessExpressionWorker(SyntaxNode? expression, SyntaxNode memberName) + => this.SyntaxGeneratorInternal.MemberAccessExpressionWorker(expression, memberName); internal SyntaxNode RefExpression(SyntaxNode expression) => SyntaxGeneratorInternal.RefExpression(expression); @@ -2285,24 +2286,26 @@ public SyntaxNode TryCastExpression(SyntaxNode expression, ITypeSymbol type) /// /// Creates an expression that denotes a type cast operation. /// - public abstract SyntaxNode CastExpression(SyntaxNode type, SyntaxNode expression); + public SyntaxNode CastExpression(SyntaxNode type, SyntaxNode expression) + => this.SyntaxGeneratorInternal.CastExpression(type, expression); /// /// Creates an expression that denotes a type cast operation. /// public SyntaxNode CastExpression(ITypeSymbol type, SyntaxNode expression) - => CastExpression(TypeExpression(type), expression); + => this.SyntaxGeneratorInternal.CastExpression(type, expression); /// /// Creates an expression that denotes a type conversion operation. /// - public abstract SyntaxNode ConvertExpression(SyntaxNode type, SyntaxNode expression); + public SyntaxNode ConvertExpression(SyntaxNode type, SyntaxNode expression) + => this.SyntaxGeneratorInternal.ConvertExpression(type, expression); /// /// Creates an expression that denotes a type conversion operation. /// public SyntaxNode ConvertExpression(ITypeSymbol type, SyntaxNode expression) - => ConvertExpression(TypeExpression(type), expression); + => this.SyntaxGeneratorInternal.ConvertExpression(type, expression); /// /// Creates an expression that declares a value returning lambda expression. diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index bbdb485e5ba35..8de73f47e1af4 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,6 +1,20 @@ *REMOVED*Microsoft.CodeAnalysis.CodeStyle.CodeStyleOptions.CodeStyleOptions() -> void +*REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.BitwiseOrExpression(Microsoft.CodeAnalysis.SyntaxNode left, Microsoft.CodeAnalysis.SyntaxNode right) -> Microsoft.CodeAnalysis.SyntaxNode +*REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.CastExpression(Microsoft.CodeAnalysis.SyntaxNode type, Microsoft.CodeAnalysis.SyntaxNode expression) -> Microsoft.CodeAnalysis.SyntaxNode +*REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.ConvertExpression(Microsoft.CodeAnalysis.SyntaxNode type, Microsoft.CodeAnalysis.SyntaxNode expression) -> Microsoft.CodeAnalysis.SyntaxNode +*REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultExpression(Microsoft.CodeAnalysis.ITypeSymbol type) -> Microsoft.CodeAnalysis.SyntaxNode +*REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultExpression(Microsoft.CodeAnalysis.SyntaxNode type) -> Microsoft.CodeAnalysis.SyntaxNode +*REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.IdentifierName(string identifier) -> Microsoft.CodeAnalysis.SyntaxNode +*REMOVED*virtual Microsoft.CodeAnalysis.Editing.SyntaxGenerator.MemberAccessExpression(Microsoft.CodeAnalysis.SyntaxNode expression, Microsoft.CodeAnalysis.SyntaxNode memberName) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.CompilationOutputInfo.GeneratedFilesOutputDirectory.get -> string Microsoft.CodeAnalysis.CompilationOutputInfo.WithGeneratedFilesOutputDirectory(string path) -> Microsoft.CodeAnalysis.CompilationOutputInfo +Microsoft.CodeAnalysis.Editing.SyntaxGenerator.BitwiseOrExpression(Microsoft.CodeAnalysis.SyntaxNode left, Microsoft.CodeAnalysis.SyntaxNode right) -> Microsoft.CodeAnalysis.SyntaxNode +Microsoft.CodeAnalysis.Editing.SyntaxGenerator.CastExpression(Microsoft.CodeAnalysis.SyntaxNode type, Microsoft.CodeAnalysis.SyntaxNode expression) -> Microsoft.CodeAnalysis.SyntaxNode +Microsoft.CodeAnalysis.Editing.SyntaxGenerator.ConvertExpression(Microsoft.CodeAnalysis.SyntaxNode type, Microsoft.CodeAnalysis.SyntaxNode expression) -> Microsoft.CodeAnalysis.SyntaxNode +Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultExpression(Microsoft.CodeAnalysis.ITypeSymbol type) -> Microsoft.CodeAnalysis.SyntaxNode +Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultExpression(Microsoft.CodeAnalysis.SyntaxNode type) -> Microsoft.CodeAnalysis.SyntaxNode +Microsoft.CodeAnalysis.Editing.SyntaxGenerator.IdentifierName(string identifier) -> Microsoft.CodeAnalysis.SyntaxNode +Microsoft.CodeAnalysis.Editing.SyntaxGenerator.MemberAccessExpression(Microsoft.CodeAnalysis.SyntaxNode expression, Microsoft.CodeAnalysis.SyntaxNode memberName) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Project.HostAnalyzerOptions.get -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions Microsoft.CodeAnalysis.ProjectInfo.WithId(Microsoft.CodeAnalysis.ProjectId id) -> Microsoft.CodeAnalysis.ProjectInfo virtual Microsoft.CodeAnalysis.Host.HostLanguageServices.Dispose() -> void diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/AttributeGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/AttributeGenerator.cs index 283dc212ff861..5746ccb5dbe43 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/AttributeGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/AttributeGenerator.cs @@ -72,14 +72,14 @@ public static SyntaxList GenerateAttributeLists( return null; var attributeArguments = GenerateAttributeArgumentList( - info.Generator, attribute, reusableSyntax); + attribute, reusableSyntax); return attribute.AttributeClass.GenerateTypeSyntax() is NameSyntax nameSyntax ? Attribute(nameSyntax, attributeArguments) : null; } private static AttributeArgumentListSyntax? GenerateAttributeArgumentList( - SyntaxGenerator generator, AttributeData attribute, AttributeSyntax? existingSyntax) + AttributeData attribute, AttributeSyntax? existingSyntax) { if (attribute.ConstructorArguments.Length == 0 && attribute.NamedArguments.Length == 0) return null; @@ -117,7 +117,7 @@ ExpressionSyntax GenerateAttributeSyntax(TypedConstant constant) } } - return ExpressionGenerator.GenerateExpression(generator, constant); + return ExpressionGenerator.GenerateExpression(constant); } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/CSharpFlagsEnumGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/CSharpFlagsEnumGenerator.cs index d6bf932c2a094..32b8c14c1e9ca 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/CSharpFlagsEnumGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/CSharpFlagsEnumGenerator.cs @@ -4,11 +4,10 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Editing; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; -internal class CSharpFlagsEnumGenerator : AbstractFlagsEnumGenerator +internal sealed class CSharpFlagsEnumGenerator : AbstractFlagsEnumGenerator { public static readonly CSharpFlagsEnumGenerator Instance = new(); @@ -16,14 +15,16 @@ private CSharpFlagsEnumGenerator() { } + protected override SyntaxGeneratorInternal SyntaxGenerator + => CSharpSyntaxGeneratorInternal.Instance; + protected override SyntaxNode CreateExplicitlyCastedLiteralValue( - SyntaxGenerator generator, INamedTypeSymbol enumType, SpecialType underlyingSpecialType, object constantValue) { var expression = ExpressionGenerator.GenerateNonEnumValueExpression( - generator, enumType.EnumUnderlyingType, constantValue, canUseFieldReference: true); + enumType.EnumUnderlyingType, constantValue, canUseFieldReference: true); var constantValueULong = underlyingSpecialType.ConvertUnderlyingValueToUInt64(constantValue); if (constantValueULong == 0) @@ -32,7 +33,7 @@ protected override SyntaxNode CreateExplicitlyCastedLiteralValue( return expression; } - return generator.CastExpression(enumType, expression); + return this.SyntaxGenerator.CastExpression(enumType, expression); } protected override bool IsValidName(INamedTypeSymbol enumType, string name) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/EnumMemberGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/EnumMemberGenerator.cs index 8e44b135cd38b..5abbf3f7fc960 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/EnumMemberGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/EnumMemberGenerator.cs @@ -62,7 +62,7 @@ public static EnumMemberDeclarationSyntax GenerateEnumMemberDeclaration( return reusableSyntax; } - var value = CreateEnumMemberValue(info.Generator, destination, enumMember); + var value = CreateEnumMemberValue(destination, enumMember); var member = EnumMemberDeclaration(enumMember.Name.ToIdentifierToken()) .WithEqualsValue(value == null ? null : EqualsValueClause(value: value)); @@ -71,7 +71,7 @@ public static EnumMemberDeclarationSyntax GenerateEnumMemberDeclaration( } private static ExpressionSyntax? CreateEnumMemberValue( - SyntaxGenerator generator, EnumDeclarationSyntax? destination, IFieldSymbol enumMember) + EnumDeclarationSyntax? destination, IFieldSymbol enumMember) { if (!enumMember.HasConstantValue) { @@ -161,7 +161,6 @@ not long and var underlyingType = namedType?.EnumUnderlyingType; return ExpressionGenerator.GenerateNonEnumValueExpression( - generator, underlyingType, enumMember.ConstantValue, canUseFieldReference: true); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ExpressionGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ExpressionGenerator.cs index 1174ff620b305..fc72ce68b0246 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ExpressionGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ExpressionGenerator.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; @@ -23,13 +22,13 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; internal static class ExpressionGenerator { public static ExpressionSyntax GenerateExpression( - SyntaxGenerator generator, TypedConstant typedConstant) + TypedConstant typedConstant) { switch (typedConstant.Kind) { case TypedConstantKind.Primitive: case TypedConstantKind.Enum: - return GenerateExpression(generator, typedConstant.Type, typedConstant.Value, canUseFieldReference: true); + return GenerateExpression(typedConstant.Type, typedConstant.Value, canUseFieldReference: true); case TypedConstantKind.Type: return typedConstant.Value is ITypeSymbol typeSymbol @@ -41,7 +40,7 @@ public static ExpressionSyntax GenerateExpression( ? GenerateNullLiteral() : ImplicitArrayCreationExpression( InitializerExpression(SyntaxKind.ArrayInitializerExpression, - [.. typedConstant.Values.Select(v => GenerateExpression(generator, v))])); + [.. typedConstant.Values.Select(v => GenerateExpression(v))])); default: return GenerateNullLiteral(); @@ -52,7 +51,6 @@ private static ExpressionSyntax GenerateNullLiteral() => LiteralExpression(SyntaxKind.NullLiteralExpression); internal static ExpressionSyntax GenerateExpression( - SyntaxGenerator generator, ITypeSymbol? type, object? value, bool canUseFieldReference) @@ -60,21 +58,21 @@ internal static ExpressionSyntax GenerateExpression( if (type != null && value != null) { if (type is INamedTypeSymbol { TypeKind: TypeKind.Enum } enumType) - return (ExpressionSyntax)CSharpFlagsEnumGenerator.Instance.CreateEnumConstantValue(generator, enumType, value); + return (ExpressionSyntax)CSharpFlagsEnumGenerator.Instance.CreateEnumConstantValue(enumType, value); if (type.IsNullable(out var underlyingType)) { // If the type of the argument is T?, then the type of the supplied default value can either be T // (e.g. int? x = 5) or it can be T? (e.g. SomeStruct? x = null). The below statement handles the case // where the type of the supplied default value is T. - return GenerateExpression(generator, underlyingType, value, canUseFieldReference); + return GenerateExpression(underlyingType, value, canUseFieldReference); } } - return GenerateNonEnumValueExpression(generator, type, value, canUseFieldReference); + return GenerateNonEnumValueExpression(type, value, canUseFieldReference); } - internal static ExpressionSyntax GenerateNonEnumValueExpression(SyntaxGenerator generator, ITypeSymbol? type, object? value, bool canUseFieldReference) + internal static ExpressionSyntax GenerateNonEnumValueExpression(ITypeSymbol? type, object? value, bool canUseFieldReference) => value switch { bool val => GenerateBooleanLiteralExpression(val), @@ -93,7 +91,7 @@ internal static ExpressionSyntax GenerateNonEnumValueExpression(SyntaxGenerator decimal val => GenerateLiteralExpression(type, val, LiteralSpecialValues.DecimalSpecialValues, formatString: null, canUseFieldReference, Literal, x => x < 0, x => -x, integerMinValueString: null), _ => type == null || type.IsReferenceType || type is IPointerTypeSymbol || type.IsNullable() ? GenerateNullLiteral() - : (ExpressionSyntax)generator.DefaultExpression(type), + : (ExpressionSyntax)CSharpSyntaxGeneratorInternal.Instance.DefaultExpression(type), }; private static ExpressionSyntax GenerateBooleanLiteralExpression(bool val) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/FieldGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/FieldGenerator.cs index f6950b2e9d564..710d68afe5e25 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/FieldGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/FieldGenerator.cs @@ -95,7 +95,7 @@ public static FieldDeclarationSyntax GenerateFieldDeclaration( var initializer = CodeGenerationFieldInfo.GetInitializer(field) is ExpressionSyntax initializerNode ? EqualsValueClause(initializerNode) - : GenerateEqualsValue(info.Generator, field); + : GenerateEqualsValue(field); var fieldDeclaration = FieldDeclaration( AttributeGenerator.GenerateAttributeLists(field.GetAttributes(), info), @@ -108,12 +108,12 @@ public static FieldDeclarationSyntax GenerateFieldDeclaration( ConditionallyAddDocumentationCommentTo(fieldDeclaration, field, info, cancellationToken)); } - private static EqualsValueClauseSyntax? GenerateEqualsValue(SyntaxGenerator generator, IFieldSymbol field) + private static EqualsValueClauseSyntax? GenerateEqualsValue(IFieldSymbol field) { if (field.HasConstantValue) { var canUseFieldReference = field.Type != null && !field.Type.Equals(field.ContainingType); - return EqualsValueClause(ExpressionGenerator.GenerateExpression(generator, field.Type, field.ConstantValue, canUseFieldReference)); + return EqualsValueClause(ExpressionGenerator.GenerateExpression(field.Type, field.ConstantValue, canUseFieldReference)); } return null; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs index 9bea07ded436a..fde1d2f232ee3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs @@ -69,7 +69,7 @@ internal static ParameterSyntax GetParameter(IParameterSymbol parameter, CSharpC .WithAttributeLists(GenerateAttributes(parameter, isExplicit, info)) .WithModifiers(GenerateModifiers(parameter, isFirstParam)) .WithType(parameter.Type.GenerateTypeSyntax(allowVar: false)) - .WithDefault(GenerateEqualsValueClause(info.Generator, parameter, isExplicit, seenOptional)); + .WithDefault(GenerateEqualsValueClause(parameter, isExplicit, seenOptional)); } private static SyntaxTokenList GenerateModifiers( @@ -88,7 +88,6 @@ parameter.ContainingSymbol is IMethodSymbol methodSymbol && } private static EqualsValueClauseSyntax? GenerateEqualsValueClause( - SyntaxGenerator generator, IParameterSymbol parameter, bool isExplicit, bool seenOptional) @@ -102,15 +101,15 @@ parameter.ContainingSymbol is IMethodSymbol methodSymbol && return null; return EqualsValueClause( - GenerateEqualsValueClauseWorker(generator, parameter, defaultValue)); + GenerateEqualsValueClauseWorker(parameter, defaultValue)); } } return null; } - private static ExpressionSyntax GenerateEqualsValueClauseWorker(SyntaxGenerator generator, IParameterSymbol parameter, object? value) - => ExpressionGenerator.GenerateExpression(generator, parameter.Type, value, canUseFieldReference: true); + private static ExpressionSyntax GenerateEqualsValueClauseWorker(IParameterSymbol parameter, object? value) + => ExpressionGenerator.GenerateExpression(parameter.Type, value, canUseFieldReference: true); private static SyntaxList GenerateAttributes( IParameterSymbol parameter, bool isExplicit, CSharpCodeGenerationContextInfo info) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs index 75d3fa56b2dce..1f253c148da03 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs @@ -14,6 +14,8 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; @@ -239,4 +241,100 @@ public override SyntaxNode UnaryPattern(SyntaxToken operatorToken, SyntaxNode pa => SyntaxFactory.UnaryPattern(operatorToken, (PatternSyntax)Parenthesize(pattern)); #endregion + + public override SyntaxNode CastExpression(SyntaxNode type, SyntaxNode expression) + => SyntaxFactory.CastExpression((TypeSyntax)type, (ExpressionSyntax)Parenthesize(expression)).WithAdditionalAnnotations(Simplifier.Annotation); + + public override SyntaxNode DefaultExpression(SyntaxNode type) + => SyntaxFactory.DefaultExpression((TypeSyntax)type).WithAdditionalAnnotations(Simplifier.Annotation); + + public override SyntaxNode DefaultExpression(ITypeSymbol type) + { + // If it's just a reference type, then "null" is the default expression for it. Note: + // this counts for actual reference type, or a type parameter with a 'class' constraint. + // Also, if it's a nullable type, then we can use "null". + if (type.IsReferenceType || + type is IPointerTypeSymbol || + type.IsNullable()) + { + return SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression); + } + + switch (type.SpecialType) + { + case SpecialType.System_Boolean: + return SyntaxFactory.LiteralExpression(SyntaxKind.FalseLiteralExpression); + case SpecialType.System_SByte: + case SpecialType.System_Byte: + case SpecialType.System_Int16: + case SpecialType.System_UInt16: + case SpecialType.System_Int32: + case SpecialType.System_UInt32: + case SpecialType.System_Int64: + case SpecialType.System_UInt64: + case SpecialType.System_Decimal: + case SpecialType.System_Single: + case SpecialType.System_Double: + return SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal("0", 0)); + } + + // Default to a "default()" expression. + return DefaultExpression(type.GenerateTypeSyntax()); + } + + public override SyntaxNode TypeExpression(ITypeSymbol typeSymbol, RefKind refKind) + { + var type = typeSymbol.GenerateTypeSyntax(); + return refKind switch + { + RefKind.Ref => SyntaxFactory.RefType(type), + RefKind.RefReadOnly => SyntaxFactory.RefType(RefKeyword, ReadOnlyKeyword, type), + _ => type, + }; + } + + public override SyntaxNode MemberAccessExpressionWorker(SyntaxNode? expression, SyntaxNode simpleName) + { + // can only be null in VB + Contract.ThrowIfNull(expression); + + return SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ParenthesizeLeft((ExpressionSyntax)expression), + (SimpleNameSyntax)simpleName); + } + + /// + /// Parenthesize the left hand size of a member access, invocation or element access expression + /// + internal static ExpressionSyntax ParenthesizeLeft(ExpressionSyntax expression) + { + if (expression is TypeSyntax || + expression.Kind() + is SyntaxKind.ThisExpression + or SyntaxKind.BaseExpression + or SyntaxKind.ParenthesizedExpression + or SyntaxKind.SimpleMemberAccessExpression + or SyntaxKind.InvocationExpression + or SyntaxKind.ElementAccessExpression + or SyntaxKind.MemberBindingExpression) + { + return expression; + } + + return (ExpressionSyntax)Parenthesize(expression); + } + + public override SyntaxNode BitwiseOrExpression(SyntaxNode left, SyntaxNode right) + => CreateBinaryExpression(SyntaxKind.BitwiseOrExpression, left, right); + + public static SyntaxNode CreateBinaryExpression(SyntaxKind syntaxKind, SyntaxNode left, SyntaxNode right) + => SyntaxFactory.BinaryExpression(syntaxKind, (ExpressionSyntax)Parenthesize(left), (ExpressionSyntax)Parenthesize(right)); + + public override SyntaxNode IdentifierName(string identifier) + => identifier.ToIdentifierName(); + + public override SyntaxNode ConvertExpression(SyntaxNode type, SyntaxNode expression) + => SyntaxFactory.CastExpression((TypeSyntax)type, (ExpressionSyntax)Parenthesize(expression)).WithAdditionalAnnotations(Simplifier.Annotation); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractFlagsEnumGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractFlagsEnumGenerator.cs index 8a7cf272e9a97..74dc1d229e1f3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractFlagsEnumGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractFlagsEnumGenerator.cs @@ -12,22 +12,24 @@ namespace Microsoft.CodeAnalysis.CodeGeneration; internal abstract class AbstractFlagsEnumGenerator : IComparer<(IFieldSymbol field, ulong value)> { - protected abstract SyntaxNode CreateExplicitlyCastedLiteralValue(SyntaxGenerator generator, INamedTypeSymbol enumType, SpecialType underlyingSpecialType, object constantValue); + protected abstract SyntaxNode CreateExplicitlyCastedLiteralValue(INamedTypeSymbol enumType, SpecialType underlyingSpecialType, object constantValue); protected abstract bool IsValidName(INamedTypeSymbol enumType, string name); - public SyntaxNode CreateEnumConstantValue(SyntaxGenerator generator, INamedTypeSymbol enumType, object constantValue) + protected abstract SyntaxGeneratorInternal SyntaxGenerator { get; } + + public SyntaxNode CreateEnumConstantValue(INamedTypeSymbol enumType, object constantValue) { // Code copied from System.Enum. var isFlagsEnum = IsFlagsEnum(enumType); if (isFlagsEnum) { - return CreateFlagsEnumConstantValue(generator, enumType, constantValue); + return CreateFlagsEnumConstantValue(enumType, constantValue); } else { // Try to see if its one of the enum values. If so, add that. Otherwise, just add // the literal value of the enum. - return CreateNonFlagsEnumConstantValue(generator, enumType, constantValue); + return CreateNonFlagsEnumConstantValue(enumType, constantValue); } } @@ -60,18 +62,17 @@ private static bool IsFlagsEnum(INamedTypeSymbol typeSymbol) return false; } - private SyntaxNode CreateFlagsEnumConstantValue(SyntaxGenerator generator, INamedTypeSymbol enumType, object constantValue) + private SyntaxNode CreateFlagsEnumConstantValue(INamedTypeSymbol enumType, object constantValue) { // These values are sorted by value. Don't change this. var allFieldsAndValues = new List<(IFieldSymbol field, ulong value)>(); GetSortedEnumFieldsAndValues(enumType, allFieldsAndValues); var usedFieldsAndValues = new List<(IFieldSymbol field, ulong value)>(); - return CreateFlagsEnumConstantValue(generator, enumType, constantValue, allFieldsAndValues, usedFieldsAndValues); + return CreateFlagsEnumConstantValue(enumType, constantValue, allFieldsAndValues, usedFieldsAndValues); } private SyntaxNode CreateFlagsEnumConstantValue( - SyntaxGenerator generator, INamedTypeSymbol enumType, object constantValue, List<(IFieldSymbol field, ulong value)> allFieldsAndValues, @@ -107,14 +108,14 @@ private SyntaxNode CreateFlagsEnumConstantValue( for (var i = usedFieldsAndValues.Count - 1; i >= 0; i--) { var field = usedFieldsAndValues[i]; - var node = CreateMemberAccessExpression(generator, field.field, enumType, underlyingSpecialType); + var node = CreateMemberAccessExpression(field.field, enumType, underlyingSpecialType); if (finalNode == null) { finalNode = node; } else { - finalNode = generator.BitwiseOrExpression(finalNode, node); + finalNode = this.SyntaxGenerator.BitwiseOrExpression(finalNode, node); } } @@ -128,28 +129,28 @@ private SyntaxNode CreateFlagsEnumConstantValue( var zeroField = GetZeroField(allFieldsAndValues); if (constantValueULong == 0 && zeroField != null) { - return CreateMemberAccessExpression(generator, zeroField, enumType, underlyingSpecialType); + return CreateMemberAccessExpression(zeroField, enumType, underlyingSpecialType); } else { // Add anything else in as a literal value. - return CreateExplicitlyCastedLiteralValue(generator, enumType, underlyingSpecialType, constantValue); + return CreateExplicitlyCastedLiteralValue(enumType, underlyingSpecialType, constantValue); } } private SyntaxNode CreateMemberAccessExpression( - SyntaxGenerator generator, IFieldSymbol field, INamedTypeSymbol enumType, SpecialType underlyingSpecialType) + IFieldSymbol field, INamedTypeSymbol enumType, SpecialType underlyingSpecialType) { if (IsValidName(enumType, field.Name)) { - return generator.MemberAccessExpression( - generator.TypeExpression(enumType), - generator.IdentifierName(field.Name)); + return this.SyntaxGenerator.MemberAccessExpression( + this.SyntaxGenerator.TypeExpression(enumType), + this.SyntaxGenerator.IdentifierName(field.Name)); } else { Contract.ThrowIfNull(field.ConstantValue); - return CreateExplicitlyCastedLiteralValue(generator, enumType, underlyingSpecialType, field.ConstantValue); + return CreateExplicitlyCastedLiteralValue(enumType, underlyingSpecialType, field.ConstantValue); } } @@ -185,7 +186,7 @@ private void GetSortedEnumFieldsAndValues( allFieldsAndValues.Sort(this); } - private SyntaxNode CreateNonFlagsEnumConstantValue(SyntaxGenerator generator, INamedTypeSymbol enumType, object constantValue) + private SyntaxNode CreateNonFlagsEnumConstantValue(INamedTypeSymbol enumType, object constantValue) { Contract.ThrowIfNull(enumType.EnumUnderlyingType); var underlyingSpecialType = enumType.EnumUnderlyingType.SpecialType; @@ -198,14 +199,12 @@ private SyntaxNode CreateNonFlagsEnumConstantValue(SyntaxGenerator generator, IN { var fieldValue = underlyingSpecialType.ConvertUnderlyingValueToUInt64(field.ConstantValue); if (constantValueULong == fieldValue) - { - return CreateMemberAccessExpression(generator, field, enumType, underlyingSpecialType); - } + return CreateMemberAccessExpression(field, enumType, underlyingSpecialType); } } // Otherwise, just add the enum as a literal. - return CreateExplicitlyCastedLiteralValue(generator, enumType, underlyingSpecialType, constantValue); + return CreateExplicitlyCastedLiteralValue(enumType, underlyingSpecialType, constantValue); } int IComparer<(IFieldSymbol field, ulong value)>.Compare((IFieldSymbol field, ulong value) x, (IFieldSymbol field, ulong value) y) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs index 5ab8c6e63bc72..271db0c4aaf77 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs @@ -2,10 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.Editing; @@ -125,4 +127,32 @@ internal static bool ParameterIsScoped(IParameterSymbol symbol) public abstract SyntaxNode UnaryPattern(SyntaxToken operatorToken, SyntaxNode pattern); #endregion + + public abstract SyntaxNode DefaultExpression(ITypeSymbol type); + public abstract SyntaxNode DefaultExpression(SyntaxNode type); + + public abstract SyntaxNode CastExpression(SyntaxNode type, SyntaxNode expression); + + public SyntaxNode CastExpression(ITypeSymbol type, SyntaxNode expression) + => CastExpression(TypeExpression(type), expression); + + public SyntaxNode TypeExpression(ITypeSymbol typeSymbol) + => TypeExpression(typeSymbol, RefKind.None); + + public abstract SyntaxNode TypeExpression(ITypeSymbol typeSymbol, RefKind refKind); + + public abstract SyntaxNode BitwiseOrExpression(SyntaxNode left, SyntaxNode right); + + public SyntaxNode MemberAccessExpression(SyntaxNode? expression, SyntaxNode memberName) + { + return MemberAccessExpressionWorker(expression, memberName) + .WithAdditionalAnnotations(Simplifier.Annotation); + } + + public abstract SyntaxNode MemberAccessExpressionWorker(SyntaxNode? expression, SyntaxNode memberName); + public abstract SyntaxNode IdentifierName(string identifier); + + public abstract SyntaxNode ConvertExpression(SyntaxNode type, SyntaxNode expression); + public SyntaxNode ConvertExpression(ITypeSymbol type, SyntaxNode expression) + => ConvertExpression(TypeExpression(type), expression); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/CodeGeneration/ExpressionGenerator.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/CodeGeneration/ExpressionGenerator.vb index 6141e1a971ad6..c350e520b28ac 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/CodeGeneration/ExpressionGenerator.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/CodeGeneration/ExpressionGenerator.vb @@ -48,7 +48,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return GenerateExpression(generator, DirectCast(type, INamedTypeSymbol).TypeArguments(0), value, canUseFieldReference) ElseIf type?.TypeKind = TypeKind.Enum Then Return DirectCast(VisualBasicFlagsEnumGenerator.Instance.CreateEnumConstantValue( - generator, DirectCast(type, INamedTypeSymbol), value), ExpressionSyntax) + DirectCast(type, INamedTypeSymbol), value), ExpressionSyntax) End If End If diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/CodeGeneration/VisualBasicFlagsEnumGenerator.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/CodeGeneration/VisualBasicFlagsEnumGenerator.vb index ebbe31d472e15..c66646e28a41c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/CodeGeneration/VisualBasicFlagsEnumGenerator.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/CodeGeneration/VisualBasicFlagsEnumGenerator.vb @@ -6,27 +6,32 @@ Imports Microsoft.CodeAnalysis.CodeGeneration Imports Microsoft.CodeAnalysis.Editing Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration - Friend Class VisualBasicFlagsEnumGenerator + Friend NotInheritable Class VisualBasicFlagsEnumGenerator Inherits AbstractFlagsEnumGenerator - Public Shared ReadOnly Instance As VisualBasicFlagsEnumGenerator = New VisualBasicFlagsEnumGenerator + Public Shared ReadOnly Instance As New VisualBasicFlagsEnumGenerator Private Sub New() End Sub + Protected Overrides ReadOnly Property SyntaxGenerator As SyntaxGeneratorInternal + Get + Return VisualBasicSyntaxGeneratorInternal.Instance + End Get + End Property + Protected Overrides Function CreateExplicitlyCastedLiteralValue( - generator As SyntaxGenerator, enumType As INamedTypeSymbol, underlyingSpecialType As SpecialType, constantValue As Object) As SyntaxNode - Dim expression = ExpressionGenerator.GenerateNonEnumValueExpression( + Dim expression = GenerateNonEnumValueExpression( enumType.EnumUnderlyingType, constantValue, canUseFieldReference:=True) Dim constantValueULong = underlyingSpecialType.ConvertUnderlyingValueToUInt64(constantValue) If constantValueULong = 0 Then Return expression End If - Return generator.ConvertExpression(enumType, expression) + Return Me.SyntaxGenerator.ConvertExpression(enumType, expression) End Function Protected Overrides Function IsValidName(enumType As INamedTypeSymbol, name As String) As Boolean diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb index c8d1a1c29926e..d67202e734f71 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb @@ -9,6 +9,7 @@ Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -515,5 +516,55 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function #End Region + + Public Overrides Function BitwiseOrExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode + Return SyntaxFactory.OrExpression(Parenthesize(left), Parenthesize(right)) + End Function + + Public Overrides Function CastExpression(type As SyntaxNode, expression As SyntaxNode) As SyntaxNode + Return SyntaxFactory.DirectCastExpression(DirectCast(expression, ExpressionSyntax), DirectCast(type, TypeSyntax)).WithAdditionalAnnotations(Simplifier.Annotation) + End Function + + Public Overrides Function DefaultExpression(type As ITypeSymbol) As SyntaxNode + Return SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword)) + End Function + + Public Overrides Function DefaultExpression(type As SyntaxNode) As SyntaxNode + Return SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword)) + End Function + + Public Overrides Function IdentifierName(identifier As String) As SyntaxNode + Return identifier.ToIdentifierName() + End Function + + Public Overrides Function MemberAccessExpressionWorker(expression As SyntaxNode, simpleName As SyntaxNode) As SyntaxNode + Return SyntaxFactory.SimpleMemberAccessExpression( + If(expression IsNot Nothing, ParenthesizeLeft(expression), Nothing), + SyntaxFactory.Token(SyntaxKind.DotToken), + DirectCast(simpleName, SimpleNameSyntax)) + End Function + + ' parenthesize the left-side of a dot or target of an invocation if not unnecessary + Public Shared Function ParenthesizeLeft(expression As SyntaxNode) As ExpressionSyntax + Dim expressionSyntax = DirectCast(expression, ExpressionSyntax) + If TypeOf expressionSyntax Is TypeSyntax _ + OrElse expressionSyntax.IsMeMyBaseOrMyClass() _ + OrElse expressionSyntax.IsKind(SyntaxKind.ParenthesizedExpression) _ + OrElse expressionSyntax.IsKind(SyntaxKind.InvocationExpression) _ + OrElse expressionSyntax.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then + Return expressionSyntax + Else + Return expressionSyntax.Parenthesize() + End If + End Function + + Public Overrides Function TypeExpression(typeSymbol As ITypeSymbol, refKind As RefKind) As SyntaxNode + ' VB doesn't support explicit ref-kinds for types. + Return typeSymbol.GenerateTypeSyntax() + End Function + + Public Overrides Function ConvertExpression(type As SyntaxNode, expression As SyntaxNode) As SyntaxNode + Return SyntaxFactory.CTypeExpression(DirectCast(expression, ExpressionSyntax), DirectCast(type, TypeSyntax)).WithAdditionalAnnotations(Simplifier.Annotation) + End Function End Class End Namespace diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index 8241c979880ef..a51af9b5cd3bf 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -132,18 +132,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return SyntaxFactory.AndExpression(Parenthesize(left), Parenthesize(right)) End Function - Public Overrides Function BitwiseOrExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.OrExpression(Parenthesize(left), Parenthesize(right)) - End Function - - Public Overrides Function CastExpression(type As SyntaxNode, expression As SyntaxNode) As SyntaxNode - Return SyntaxFactory.DirectCastExpression(DirectCast(expression, ExpressionSyntax), DirectCast(type, TypeSyntax)).WithAdditionalAnnotations(Simplifier.Annotation) - End Function - - Public Overrides Function ConvertExpression(type As SyntaxNode, expression As SyntaxNode) As SyntaxNode - Return SyntaxFactory.CTypeExpression(DirectCast(expression, ExpressionSyntax), DirectCast(type, TypeSyntax)).WithAdditionalAnnotations(Simplifier.Annotation) - End Function - Public Overrides Function ConditionalExpression(condition As SyntaxNode, whenTrue As SyntaxNode, whenFalse As SyntaxNode) As SyntaxNode Return SyntaxFactory.TernaryConditionalExpression( DirectCast(condition, ExpressionSyntax), @@ -163,16 +151,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return SyntaxFactory.Literal(text, value) End Function - Public Overrides Function DefaultExpression(type As ITypeSymbol) As SyntaxNode - Return SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword)) - End Function - - Public Overrides Function DefaultExpression(type As SyntaxNode) As SyntaxNode - Return SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword)) - End Function - Public Overloads Overrides Function ElementAccessExpression(expression As SyntaxNode, arguments As IEnumerable(Of SyntaxNode)) As SyntaxNode - Return SyntaxFactory.InvocationExpression(ParenthesizeLeft(expression), CreateArgumentList(arguments)) + Return SyntaxFactory.InvocationExpression(VisualBasicSyntaxGeneratorInternal.ParenthesizeLeft(expression), CreateArgumentList(arguments)) End Function Public Overrides Function ExpressionStatement(expression As SyntaxNode) As SyntaxNode @@ -194,10 +174,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration SyntaxFactory.SeparatedList(typeArguments.Cast(Of TypeSyntax)()))).WithAdditionalAnnotations(Simplifier.Annotation) End Function - Public Overrides Function IdentifierName(identifier As String) As SyntaxNode - Return identifier.ToIdentifierName() - End Function - Public Overrides Function IfStatement(condition As SyntaxNode, trueStatements As IEnumerable(Of SyntaxNode), Optional falseStatements As IEnumerable(Of SyntaxNode) = Nothing) As SyntaxNode Dim ifStmt = SyntaxFactory.IfStatement(SyntaxFactory.Token(SyntaxKind.IfKeyword), @@ -250,7 +226,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Public Overloads Overrides Function InvocationExpression(expression As SyntaxNode, arguments As IEnumerable(Of SyntaxNode)) As SyntaxNode - Return SyntaxFactory.InvocationExpression(ParenthesizeLeft(expression), CreateArgumentList(arguments)) + Return SyntaxFactory.InvocationExpression(VisualBasicSyntaxGeneratorInternal.ParenthesizeLeft(expression), CreateArgumentList(arguments)) End Function Public Overrides Function IsTypeExpression(expression As SyntaxNode, type As SyntaxNode) As SyntaxNode @@ -273,13 +249,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return SyntaxFactory.OrElseExpression(Parenthesize(left), Parenthesize(right)) End Function - Friend Overrides Function MemberAccessExpressionWorker(expression As SyntaxNode, simpleName As SyntaxNode) As SyntaxNode - Return SyntaxFactory.SimpleMemberAccessExpression( - If(expression IsNot Nothing, ParenthesizeLeft(expression), Nothing), - SyntaxFactory.Token(SyntaxKind.DotToken), - DirectCast(simpleName, SimpleNameSyntax)) - End Function - Public Overrides Function ConditionalAccessExpression(expression As SyntaxNode, whenNotNull As SyntaxNode) As SyntaxNode Return SyntaxGeneratorInternal.ConditionalAccessExpression(expression, whenNotNull) End Function @@ -293,20 +262,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration SyntaxFactory.ArgumentList(CType(SyntaxFactory.SeparatedList(arguments), SeparatedSyntaxList(Of ArgumentSyntax)))) End Function - ' parenthesize the left-side of a dot or target of an invocation if not unnecessary - Private Shared Function ParenthesizeLeft(expression As SyntaxNode) As ExpressionSyntax - Dim expressionSyntax = DirectCast(expression, ExpressionSyntax) - If TypeOf expressionSyntax Is TypeSyntax _ - OrElse expressionSyntax.IsMeMyBaseOrMyClass() _ - OrElse expressionSyntax.IsKind(SyntaxKind.ParenthesizedExpression) _ - OrElse expressionSyntax.IsKind(SyntaxKind.InvocationExpression) _ - OrElse expressionSyntax.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then - Return expressionSyntax - Else - Return expressionSyntax.Parenthesize() - End If - End Function - Public Overrides Function MultiplyExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode Return SyntaxFactory.MultiplyExpression(Parenthesize(left), Parenthesize(right)) End Function @@ -383,11 +338,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return namespaceOrTypeSymbol.GenerateTypeSyntax() End Function - Private Protected Overrides Function TypeExpression(typeSymbol As ITypeSymbol, refKind As RefKind) As SyntaxNode - ' VB doesn't support explicit ref-kinds for types. - Return typeSymbol.GenerateTypeSyntax() - End Function - Public Overrides Function TypeExpression(specialType As SpecialType) As SyntaxNode Select Case specialType Case SpecialType.System_Boolean