/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/CSharp/Impl/CodeModel/MethodXml/MethodXmlBuilder.cs
613 строк
19 KB
Cyrus Najmabadi
Use file scoped namespaces. (#77854)
27 мар 2025, 09:55
Не верифицирован
27 мар 2025, 09:55
98d41b8
Код
Авторство
О чём код?
// 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. #nullable disable using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.MethodXml; namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel.MethodXml; internal sealed class MethodXmlBuilder : AbstractMethodXmlBuilder { private MethodXmlBuilder(IMethodSymbol symbol, SemanticModel semanticModel) : base(symbol, semanticModel) { } private void GenerateBlock(BlockSyntax block) { using (BlockTag()) { foreach (var statement in block.Statements) { GenerateComments(statement.GetLeadingTrivia()); GenerateStatement(statement); } // Handle any additional comments now, but only comments within the extent of this block. GenerateComments(block.CloseBraceToken.LeadingTrivia); } } private void GenerateComments(SyntaxTriviaList triviaList) { foreach (var trivia in triviaList) { // Multi-line comment forms are ignored. if (trivia.Kind() == SyntaxKind.SingleLineCommentTrivia) { // In order to be valid, the comment must appear on its own line. var line = Text.Lines.GetLineFromPosition(trivia.SpanStart); var firstNonWhitespacePosition = line.GetFirstNonWhitespacePosition() ?? -1; if (firstNonWhitespacePosition == trivia.SpanStart) { using var tag = CommentTag(); // Skip initial slashes var trimmedComment = trivia.ToString()[2..]; EncodedText(trimmedComment); } } } } private void GenerateStatement(StatementSyntax statement) { var success = false; var mark = GetMark(); switch (statement.Kind()) { case SyntaxKind.LocalDeclarationStatement: success = TryGenerateLocal((LocalDeclarationStatementSyntax)statement); break; case SyntaxKind.Block: success = true; GenerateBlock((BlockSyntax)statement); break; case SyntaxKind.ExpressionStatement: success = TryGenerateExpressionStatement((ExpressionStatementSyntax)statement); break; } if (!success) { Rewind(mark); GenerateUnknown(statement); } // Just for readability LineBreak(); } private bool TryGenerateLocal(LocalDeclarationStatementSyntax localDeclarationStatement) { /* - <ElementType name="Local" content="eltOnly"> <attribute type="id" /> <attribute type="static" /> <attribute type="instance" /> <attribute type="implicit" /> <attribute type="constant" /> - <group order="one"> <element type="Type" /> <element type="ArrayType" /> </group> - <group minOccurs="1" maxOccurs="*" order="seq"> <element type="LocalName" /> <element type="Expression" minOccurs="0" maxOccurs="1" /> </group> </ElementType> */ using (LocalTag(GetLineNumber(localDeclarationStatement))) { // Spew the type first if (!TryGenerateType(localDeclarationStatement.Declaration.Type)) { return false; } // Now spew the list of variables foreach (var variable in localDeclarationStatement.Declaration.Variables) { GenerateName(variable.Identifier.ToString()); if (variable.Initializer != null) { if (!TryGenerateExpression(variable.Initializer.Value)) { return false; } } } } return true; } private bool TryGenerateExpressionStatement(ExpressionStatementSyntax expressionStatement) { using (ExpressionStatementTag(GetLineNumber(expressionStatement))) { return TryGenerateExpression(expressionStatement.Expression); } } private bool TryGenerateType(TypeSyntax type) { var typeSymbol = SemanticModel.GetTypeInfo(type).Type; if (typeSymbol == null) { return false; } GenerateType(typeSymbol); return true; } private bool TryGenerateExpression(ExpressionSyntax expression) { using (ExpressionTag()) { return TryGenerateExpressionSansTag(expression); } } private bool TryGenerateExpressionSansTag(ExpressionSyntax expression) { switch (expression.Kind()) { case SyntaxKind.CharacterLiteralExpression: return TryGenerateCharLiteral(expression); case SyntaxKind.UnaryMinusExpression: case SyntaxKind.NumericLiteralExpression: case SyntaxKind.StringLiteralExpression: case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: return TryGenerateLiteral(expression); case SyntaxKind.NullLiteralExpression: GenerateNullLiteral(); return true; case SyntaxKind.ParenthesizedExpression: return TryGenerateParentheses((ParenthesizedExpressionSyntax)expression); case SyntaxKind.AddExpression: case SyntaxKind.BitwiseOrExpression: case SyntaxKind.BitwiseAndExpression: return TryGenerateBinaryOperation((BinaryExpressionSyntax)expression); case SyntaxKind.SimpleAssignmentExpression: case SyntaxKind.AddAssignmentExpression: return TryGenerateAssignment((AssignmentExpressionSyntax)expression); case SyntaxKind.CastExpression: return TryGenerateCast((CastExpressionSyntax)expression); case SyntaxKind.ObjectCreationExpression: return TryGenerateNewClass((ObjectCreationExpressionSyntax)expression); case SyntaxKind.ArrayCreationExpression: return TryGenerateNewArray((ArrayCreationExpressionSyntax)expression); case SyntaxKind.ArrayInitializerExpression: return TryGenerateArrayLiteral((InitializerExpressionSyntax)expression); case SyntaxKind.SimpleMemberAccessExpression: return TryGenerateNameRef((MemberAccessExpressionSyntax)expression); case SyntaxKind.IdentifierName: return TryGenerateNameRef((IdentifierNameSyntax)expression); case SyntaxKind.InvocationExpression: return GenerateMethodCall((InvocationExpressionSyntax)expression); case SyntaxKind.ElementAccessExpression: return TryGenerateArrayElementAccess((ElementAccessExpressionSyntax)expression); case SyntaxKind.TypeOfExpression: return TryGenerateTypeOfExpression((TypeOfExpressionSyntax)expression); case SyntaxKind.ThisExpression: GenerateThisReference(); return true; case SyntaxKind.BaseExpression: GenerateBaseReference(); return true; } return false; } private bool TryGenerateLiteral(ExpressionSyntax expression) { /* <ElementType name="Literal" content="eltOnly"> - <group order="one"> <element type="Null" /> <element type="Number" /> <element type="Boolean" /> <element type="Char" /> <element type="String" /> <element type="Array" /> <element type="Type" /> </group> </ElementType> */ using (LiteralTag()) { var constantValue = SemanticModel.GetConstantValue(expression); if (!constantValue.HasValue) { return false; } var type = SemanticModel.GetTypeInfo(expression).Type; if (type == null) { return false; } switch (expression.Kind()) { case SyntaxKind.UnaryMinusExpression: case SyntaxKind.NumericLiteralExpression: GenerateNumber(constantValue.Value, type); return true; case SyntaxKind.StringLiteralExpression: GenerateString((string)constantValue.Value); return true; case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: GenerateBoolean((bool)constantValue.Value); return true; } return false; } } private bool TryGenerateCharLiteral(ExpressionSyntax expression) { // For non-letters and digits, generate a cast of the numeric value to a char. // Otherwise, we might end up generating invalid XML. if (expression.Kind() != SyntaxKind.CharacterLiteralExpression) { return false; } var constantValue = SemanticModel.GetConstantValue(expression); if (!constantValue.HasValue) { return false; } var ch = (char)constantValue.Value; if (!char.IsLetterOrDigit(ch)) { using (CastTag()) { GenerateType(SpecialType.System_Char); using (ExpressionTag()) using (LiteralTag()) { GenerateNumber((ushort)ch, SpecialType.System_UInt16); } } } else { using (LiteralTag()) { GenerateChar(ch); } } return true; } private bool TryGenerateParentheses(ParenthesizedExpressionSyntax parenthesizedExpression) { using (ParenthesesTag()) { return TryGenerateExpression(parenthesizedExpression.Expression); } } private bool TryGenerateBinaryOperation(BinaryExpressionSyntax binaryExpression) { BinaryOperatorKind kind; switch (binaryExpression.Kind()) { case SyntaxKind.AddExpression: kind = BinaryOperatorKind.Plus; break; case SyntaxKind.BitwiseOrExpression: kind = BinaryOperatorKind.BitwiseOr; break; case SyntaxKind.BitwiseAndExpression: kind = BinaryOperatorKind.BitwiseAnd; break; default: return false; } using (BinaryOperationTag(kind)) { return TryGenerateExpression(binaryExpression.Left) && TryGenerateExpression(binaryExpression.Right); } } private bool TryGenerateAssignment(AssignmentExpressionSyntax binaryExpression) { var kind = BinaryOperatorKind.None; switch (binaryExpression.Kind()) { case SyntaxKind.AddAssignmentExpression: kind = BinaryOperatorKind.AddDelegate; break; } using (AssignmentTag(kind)) { return TryGenerateExpression(binaryExpression.Left) && TryGenerateExpression(binaryExpression.Right); } } private bool TryGenerateCast(CastExpressionSyntax castExpression) { var type = SemanticModel.GetTypeInfo(castExpression.Type).Type; if (type == null) { return false; } using (CastTag()) { GenerateType(type); return TryGenerateExpression(castExpression.Expression); } } private bool TryGenerateNewClass(ObjectCreationExpressionSyntax objectCreationExpression) { if (SemanticModel.GetSymbolInfo(objectCreationExpression.Type).Symbol is not ITypeSymbol type) { return false; } using (NewClassTag()) { GenerateType(type); foreach (var argument in objectCreationExpression.ArgumentList.Arguments) { if (!TryGenerateArgument(argument)) { return false; } } } return true; } private bool TryGenerateNewArray(ArrayCreationExpressionSyntax arrayCreationExpression) { var type = SemanticModel.GetTypeInfo(arrayCreationExpression).Type; if (type == null) { return false; } using (NewArrayTag()) { GenerateType(type); if (arrayCreationExpression.Initializer != null) { using (BoundTag()) using (ExpressionTag()) using (LiteralTag()) using (NumberTag()) { EncodedText(arrayCreationExpression.Initializer.Expressions.Count.ToString()); } if (!TryGenerateExpression(arrayCreationExpression.Initializer)) { return false; } } else { foreach (var rankSpecifier in arrayCreationExpression.Type.RankSpecifiers) { foreach (var size in rankSpecifier.Sizes) { using (BoundTag()) { if (!TryGenerateExpression(size)) { return false; } } } } } } return true; } private bool TryGenerateArrayLiteral(InitializerExpressionSyntax initializerExpression) { using (LiteralTag()) using (ArrayTag()) { foreach (var expression in initializerExpression.Expressions) { if (!TryGenerateExpression(expression)) { return false; } } } return true; } private bool TryGenerateNameRef(MemberAccessExpressionSyntax memberAccessExpression) { var symbol = SemanticModel.GetSymbolInfo(memberAccessExpression).Symbol; // No null check for 'symbol' here. If 'symbol' unknown, we'll // generate an "unknown" name ref. using (NameRefTag(GetVariableKind(symbol))) { var leftHandSymbol = SemanticModel.GetSymbolInfo(memberAccessExpression.Expression).Symbol; if (leftHandSymbol is IAliasSymbol alias) leftHandSymbol = alias.Target; // If the left-hand side is a named type, we generate a literal expression // with the type name. Otherwise, we generate the expression normally. if (leftHandSymbol is INamedTypeSymbol namedType) { using (ExpressionTag()) using (LiteralTag()) { GenerateType(namedType); } } else if (!TryGenerateExpression(memberAccessExpression.Expression)) { return false; } GenerateName(memberAccessExpression.Name.Identifier.ValueText); } return true; } private bool TryGenerateNameRef(IdentifierNameSyntax identifierName) { var symbol = SemanticModel.GetSymbolInfo(identifierName).Symbol; // No null check for 'symbol' here. If 'symbol' unknown, we'll // generate an "unknown" name ref. var variableKind = GetVariableKind(symbol); using (NameRefTag(variableKind)) { if (symbol != null && variableKind != VariableKind.Local) { using (ExpressionTag()) { GenerateThisReference(); } } GenerateName(identifierName.Identifier.ToString()); } return true; } private bool GenerateMethodCall(InvocationExpressionSyntax invocationExpression) { using (MethodCallTag()) { if (!TryGenerateExpression(invocationExpression.Expression)) { return false; } foreach (var argument in invocationExpression.ArgumentList.Arguments) { if (!TryGenerateArgument(argument)) { return false; } } } return true; } private bool TryGenerateTypeOfExpression(TypeOfExpressionSyntax typeOfExpression) { if (typeOfExpression.Type == null) { return false; } var type = SemanticModel.GetTypeInfo(typeOfExpression.Type).Type; if (type == null) { return false; } GenerateType(type); return true; } private bool TryGenerateArrayElementAccess(ElementAccessExpressionSyntax elementAccessExpression) { using (ArrayElementAccessTag()) { if (!TryGenerateExpression(elementAccessExpression.Expression)) { return false; } foreach (var argument in elementAccessExpression.ArgumentList.Arguments) { if (!TryGenerateExpression(argument.Expression)) { return false; } } } return true; } private bool TryGenerateArgument(ArgumentSyntax argument) { using (ArgumentTag()) { return TryGenerateExpression(argument.Expression); } } public static string Generate(MethodDeclarationSyntax methodDeclaration, SemanticModel semanticModel) { var symbol = semanticModel.GetDeclaredSymbol(methodDeclaration); var builder = new MethodXmlBuilder(symbol, semanticModel); builder.GenerateBlock(methodDeclaration.Body); return builder.ToString(); } }