|
// Licensed to the .NET Foundation under one or more agreements.
// 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.Collections.Generic;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageService;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host.Mef;
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;
using static CSharpSyntaxTokens;
[ExportLanguageService(typeof(SyntaxGeneratorInternal), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Incorrectly used in production code: https://github.com/dotnet/roslyn/issues/42839")]
internal sealed class CSharpSyntaxGeneratorInternal() : SyntaxGeneratorInternal
{
public static readonly SyntaxGeneratorInternal Instance = new CSharpSyntaxGeneratorInternal();
public override ISyntaxFacts SyntaxFacts
=> CSharpSyntaxFacts.Instance;
public override SyntaxTrivia CarriageReturnLineFeed
=> SyntaxFactory.CarriageReturnLineFeed;
public override SyntaxTrivia ElasticCarriageReturnLineFeed
=> SyntaxFactory.ElasticCarriageReturnLineFeed;
public override bool SupportsThrowExpression()
=> true;
public override bool RequiresExplicitImplementationForInterfaceMembers
=> false;
public override SyntaxTrivia EndOfLine(string text)
=> SyntaxFactory.EndOfLine(text);
public override SyntaxTrivia SingleLineComment(string text)
=> SyntaxFactory.Comment("//" + text);
public override SyntaxNode LocalDeclarationStatement(SyntaxNode? type, SyntaxToken name, SyntaxNode? initializer, bool isConst)
{
return SyntaxFactory.LocalDeclarationStatement(
isConst ? [ConstKeyword] : default,
VariableDeclaration(type, name, initializer));
}
public override SyntaxNode WithInitializer(SyntaxNode variableDeclarator, SyntaxNode initializer)
=> ((VariableDeclaratorSyntax)variableDeclarator).WithInitializer((EqualsValueClauseSyntax)initializer);
public override SyntaxNode EqualsValueClause(SyntaxToken operatorToken, SyntaxNode value)
=> SyntaxFactory.EqualsValueClause(operatorToken, (ExpressionSyntax)value);
internal static VariableDeclarationSyntax VariableDeclaration(SyntaxNode? type, SyntaxToken name, SyntaxNode? expression)
{
return SyntaxFactory.VariableDeclaration(
type == null ? SyntaxFactory.IdentifierName("var") : (TypeSyntax)type,
[SyntaxFactory.VariableDeclarator(
name, argumentList: null,
expression == null ? null : SyntaxFactory.EqualsValueClause((ExpressionSyntax)expression))]);
}
public override SyntaxToken Identifier(string identifier)
=> SyntaxFactory.Identifier(identifier);
public override SyntaxNode ConditionalAccessExpression(SyntaxNode expression, SyntaxNode whenNotNull)
=> SyntaxFactory.ConditionalAccessExpression((ExpressionSyntax)expression, (ExpressionSyntax)whenNotNull);
public override SyntaxNode MemberBindingExpression(SyntaxNode name)
=> SyntaxFactory.MemberBindingExpression((SimpleNameSyntax)name);
public override SyntaxNode RefExpression(SyntaxNode expression)
=> SyntaxFactory.RefExpression((ExpressionSyntax)expression);
public override SyntaxNode AddParentheses(SyntaxNode expressionOrPattern, bool includeElasticTrivia = true, bool addSimplifierAnnotation = true)
=> Parenthesize(expressionOrPattern, includeElasticTrivia, addSimplifierAnnotation);
internal static SyntaxNode Parenthesize(SyntaxNode expressionOrPattern, bool includeElasticTrivia = true, bool addSimplifierAnnotation = true)
=> expressionOrPattern switch
{
ExpressionSyntax expression => expression.Parenthesize(includeElasticTrivia, addSimplifierAnnotation),
PatternSyntax pattern => pattern.Parenthesize(includeElasticTrivia, addSimplifierAnnotation),
var other => other,
};
public override SyntaxNode YieldReturnStatement(SyntaxNode expression)
=> SyntaxFactory.YieldStatement(SyntaxKind.YieldReturnStatement, (ExpressionSyntax)expression);
/// <summary>
/// C# always requires a type to be present with a local declaration. (Even if that type is
/// <c>var</c>).
/// </summary>
public override bool RequiresLocalDeclarationType() => true;
public override SyntaxNode InterpolatedStringExpression(SyntaxToken startToken, IEnumerable<SyntaxNode> content, SyntaxToken endToken)
=> SyntaxFactory.InterpolatedStringExpression(startToken, [.. content.Cast<InterpolatedStringContentSyntax>()], endToken);
public override SyntaxNode InterpolatedStringText(SyntaxToken textToken)
=> SyntaxFactory.InterpolatedStringText(textToken);
public override SyntaxToken InterpolatedStringTextToken(string content, string value)
=> SyntaxFactory.Token(
[],
SyntaxKind.InterpolatedStringTextToken,
content, value,
[]);
public override SyntaxNode Interpolation(SyntaxNode syntaxNode)
=> SyntaxFactory.Interpolation((ExpressionSyntax)syntaxNode);
public override SyntaxNode InterpolationAlignmentClause(SyntaxNode alignment)
=> SyntaxFactory.InterpolationAlignmentClause(CommaToken, (ExpressionSyntax)alignment);
public override SyntaxNode InterpolationFormatClause(string format)
=> SyntaxFactory.InterpolationFormatClause(
ColonToken,
SyntaxFactory.Token(default, SyntaxKind.InterpolatedStringTextToken, format, format, default));
public override SyntaxNode TypeParameterList(IEnumerable<string> typeParameterNames)
=> SyntaxFactory.TypeParameterList([.. typeParameterNames.Select(SyntaxFactory.TypeParameter)]);
internal static SyntaxTokenList GetParameterModifiers(
IParameterSymbol parameter, bool forFunctionPointerReturnParameter = false)
=> GetParameterModifiers(ParameterIsScoped(parameter), parameter.RefKind, parameter.IsParams, forFunctionPointerReturnParameter);
internal static SyntaxTokenList GetParameterModifiers(
bool isScoped, RefKind refKind, bool isParams, bool forFunctionPointerReturnParameter = false)
{
using var _ = ArrayBuilder<SyntaxToken>.GetInstance(out var result);
if (isScoped)
result.Add(ScopedKeyword);
switch (refKind)
{
case RefKind.Out:
result.Add(OutKeyword);
break;
case RefKind.Ref:
result.Add(RefKeyword);
break;
// Note: RefKind.RefReadonly == RefKind.In. Function Pointers must use the correct
// ref kind syntax when generating for the return parameter vs other parameters.
// The return parameter must use ref readonly, like regular methods.
case RefKind.In when !forFunctionPointerReturnParameter:
result.Add(InKeyword);
break;
case RefKind.RefReadOnly when forFunctionPointerReturnParameter:
result.Add(RefKeyword);
result.Add(ReadOnlyKeyword);
break;
case RefKind.RefReadOnlyParameter:
result.Add(RefKeyword);
result.Add(ReadOnlyKeyword);
break;
}
if (isParams)
result.Add(ParamsKeyword);
return SyntaxFactory.TokenList(result);
}
public override SyntaxNode Type(ITypeSymbol typeSymbol, bool typeContext)
=> typeContext ? typeSymbol.GenerateTypeSyntax() : typeSymbol.GenerateExpressionSyntax();
public override SyntaxNode NegateEquality(SyntaxGenerator generator, SyntaxNode binaryExpression, SyntaxNode left, BinaryOperatorKind negatedKind, SyntaxNode right)
=> negatedKind switch
{
BinaryOperatorKind.Equals => generator.ReferenceEqualsExpression(left, right),
BinaryOperatorKind.NotEquals => generator.ReferenceNotEqualsExpression(left, right),
_ => throw ExceptionUtilities.UnexpectedValue(negatedKind),
};
public override SyntaxNode IsNotTypeExpression(SyntaxNode expression, SyntaxNode type)
=> throw ExceptionUtilities.Unreachable();
#region Patterns
public override bool SupportsPatterns(ParseOptions options)
=> ((CSharpParseOptions)options).LanguageVersion >= LanguageVersion.CSharp7;
public override SyntaxNode IsPatternExpression(SyntaxNode expression, SyntaxToken isKeyword, SyntaxNode pattern)
=> SyntaxFactory.IsPatternExpression(
(ExpressionSyntax)expression,
isKeyword == default ? IsKeyword : isKeyword,
(PatternSyntax)pattern);
public override SyntaxNode AndPattern(SyntaxNode left, SyntaxNode right)
=> SyntaxFactory.BinaryPattern(SyntaxKind.AndPattern, (PatternSyntax)Parenthesize(left), (PatternSyntax)Parenthesize(right));
public override SyntaxNode ConstantPattern(SyntaxNode expression)
=> SyntaxFactory.ConstantPattern((ExpressionSyntax)expression);
public override SyntaxNode DeclarationPattern(INamedTypeSymbol type, string name)
=> SyntaxFactory.DeclarationPattern(
type.GenerateTypeSyntax(),
SyntaxFactory.SingleVariableDesignation(name.ToIdentifierToken()));
public override SyntaxNode LessThanRelationalPattern(SyntaxNode expression)
=> SyntaxFactory.RelationalPattern(LessThanToken, (ExpressionSyntax)expression);
public override SyntaxNode LessThanEqualsRelationalPattern(SyntaxNode expression)
=> SyntaxFactory.RelationalPattern(LessThanEqualsToken, (ExpressionSyntax)expression);
public override SyntaxNode GreaterThanRelationalPattern(SyntaxNode expression)
=> SyntaxFactory.RelationalPattern(GreaterThanToken, (ExpressionSyntax)expression);
public override SyntaxNode GreaterThanEqualsRelationalPattern(SyntaxNode expression)
=> SyntaxFactory.RelationalPattern(GreaterThanEqualsToken, (ExpressionSyntax)expression);
public override SyntaxNode NotPattern(SyntaxNode pattern)
=> SyntaxFactory.UnaryPattern(NotKeyword, (PatternSyntax)Parenthesize(pattern));
public override SyntaxNode OrPattern(SyntaxNode left, SyntaxNode right)
=> SyntaxFactory.BinaryPattern(SyntaxKind.OrPattern, (PatternSyntax)Parenthesize(left), (PatternSyntax)Parenthesize(right));
public override SyntaxNode ParenthesizedPattern(SyntaxNode pattern)
=> Parenthesize(pattern);
public override SyntaxNode TypePattern(SyntaxNode type)
=> SyntaxFactory.TypePattern((TypeSyntax)type);
public override SyntaxNode UnaryPattern(SyntaxToken operatorToken, SyntaxNode pattern)
=> 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(<typename>)" 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);
}
/// <summary>
/// Parenthesize the left hand size of a member access, invocation or element access expression
/// </summary>
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);
}
|