/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateMethodService.State.cs
261 строка
11 KB
Cyrus Najmabadi
Reapply "Update methods to be `async`." (#81808)
31 дек 2025, 02:04
31 дек 2025, 02:04
ba729ad
Код
Авторство
О чём код?
// 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 System; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; internal abstract partial class AbstractGenerateMethodService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax> { internal new sealed class State : AbstractGenerateParameterizedMemberService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>.State { public static async Task<State> GenerateMethodStateAsync( TService service, SemanticDocument document, SyntaxNode interfaceNode, CancellationToken cancellationToken) { var state = new State(); if (!await state.TryInitializeMethodAsync(service, document, interfaceNode, cancellationToken).ConfigureAwait(false)) { return null; } return state; } private async Task<bool> TryInitializeMethodAsync( TService service, SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken) { // Cases that we deal with currently: // // 1) expr.Goo // 2) expr->Goo // 3) Goo // 4) expr.Goo() // 5) expr->Goo() // 6) Goo() // 7) ReturnType Explicit.Interface.Goo() // // In the first 3 invocationExpressionOpt will be null and we'll have to infer a // delegate type in order to figure out the right method signature to generate. In // the next 3 invocationExpressionOpt will be non null and will be used to figure // out the types/name of the parameters to generate. In the last one, we're going to // generate into an interface. if (service.IsExplicitInterfaceGeneration(node)) { if (!TryInitializeExplicitInterface(service, document, node, cancellationToken)) { return false; } } else if (service.IsSimpleNameGeneration(node)) { if (!TryInitializeSimpleName(service, document, (TSimpleNameSyntax)node, cancellationToken)) { return false; } } return await TryFinishInitializingStateAsync(service, document, cancellationToken).ConfigureAwait(false); } private bool TryInitializeExplicitInterface( TService service, SemanticDocument document, SyntaxNode methodDeclaration, CancellationToken cancellationToken) { MethodKind = MethodKind.Ordinary; if (!service.TryInitializeExplicitInterfaceState( document, methodDeclaration, cancellationToken, out var identifierToken, out var methodSymbol, out var typeToGenerateIn)) { return false; } if (methodSymbol.ExplicitInterfaceImplementations.Any()) { return false; } IdentifierToken = identifierToken; TypeToGenerateIn = typeToGenerateIn; cancellationToken.ThrowIfCancellationRequested(); var semanticModel = document.SemanticModel; ContainingType = semanticModel.GetEnclosingNamedType(methodDeclaration.SpanStart, cancellationToken); if (ContainingType == null) { return false; } if (!ContainingType.Interfaces.Contains(TypeToGenerateIn)) { return false; } SignatureInfo = new MethodSignatureInfo(document, this, methodSymbol); return true; } private bool TryInitializeSimpleName( TService service, SemanticDocument semanticDocument, TSimpleNameSyntax simpleName, CancellationToken cancellationToken) { MethodKind = MethodKind.Ordinary; SimpleNameOpt = simpleName; if (!service.TryInitializeSimpleNameState( semanticDocument, simpleName, cancellationToken, out var identifierToken, out var simpleNameOrMemberAccessExpression, out var invocationExpressionOpt, out var isInConditionalExpression)) { return false; } var syntaxFacts = semanticDocument.Document.GetRequiredLanguageService<ISyntaxFactsService>(); if (syntaxFacts.IsLeftSideOfAnyAssignment(simpleNameOrMemberAccessExpression)) return false; IdentifierToken = identifierToken; SimpleNameOrMemberAccessExpression = simpleNameOrMemberAccessExpression; InvocationExpressionOpt = invocationExpressionOpt; IsInConditionalAccessExpression = isInConditionalExpression; if (string.IsNullOrWhiteSpace(IdentifierToken.ValueText)) { return false; } // If we're not in a type, don't even bother. NOTE(cyrusn): We'll have to rethink this // for C# Script. cancellationToken.ThrowIfCancellationRequested(); var semanticModel = semanticDocument.SemanticModel; ContainingType = semanticModel.GetEnclosingNamedType(SimpleNameOpt.SpanStart, cancellationToken); if (ContainingType == null) { return false; } if (InvocationExpressionOpt != null) { SignatureInfo = service.CreateInvocationMethodInfo(semanticDocument, this); } else { var typeInference = semanticDocument.Document.GetLanguageService<ITypeInferenceService>(); var delegateInvokeMethod = typeInference.InferDelegateType(semanticModel, SimpleNameOrMemberAccessExpression, cancellationToken)?.DelegateInvokeMethod; if (delegateInvokeMethod != null) { // If we inferred Func/Action here, attempt to create better parameter names than the default // 'arg1/arg2/arg3' form that the delegate specifies. var parameterNames = delegateInvokeMethod.ContainingType is { Name: nameof(Action) or nameof(Func<>), ContainingNamespace.Name: nameof(System) } ? GenerateParameterNamesBasedOnParameterTypes(delegateInvokeMethod.Parameters) : delegateInvokeMethod.Parameters.SelectAsArray(p => p.Name); SignatureInfo = new MethodSignatureInfo(semanticDocument, this, delegateInvokeMethod, parameterNames); } else { // We don't have and invocation expression or a delegate, but we may have a special expression without parenthesis. Lets see // if the type inference service can directly infer the type for our expression. var expressionType = service.DetermineReturnTypeForSimpleNameOrMemberAccessExpression(typeInference, semanticModel, SimpleNameOrMemberAccessExpression, cancellationToken); if (expressionType == null) return false; SignatureInfo = new MethodSignatureInfo(semanticDocument, this, CreateMethodSymbolWithReturnType(expressionType)); } } // Now, try to bind the invocation and see if it succeeds or not. if it succeeds and // binds uniquely, then we don't need to offer this quick fix. cancellationToken.ThrowIfCancellationRequested(); // If the name bound with errors, then this is a candidate for generate method. var semanticInfo = semanticModel.GetSymbolInfo(SimpleNameOrMemberAccessExpression, cancellationToken); if (semanticInfo.GetAllSymbols().Any(static s => s.Kind is SymbolKind.Local or SymbolKind.Parameter) && !service.AreSpecialOptionsActive(semanticModel)) { // if the name bound to something in scope then we don't want to generate the // method because it will be shadowed by what's in scope. Unless we are in a // special state such as Option Strict On where we want to generate fixes even // if we shadow types. return false; } // Check if the symbol is on the list of valid symbols for this language. cancellationToken.ThrowIfCancellationRequested(); if (semanticInfo.Symbol != null && !service.IsValidSymbol(semanticInfo.Symbol, semanticModel)) { return false; } // Either we found no matches, or this was ambiguous. Either way, we might be able // to generate a method here. Determine where the user wants to generate the method // into, and if it's valid then proceed. cancellationToken.ThrowIfCancellationRequested(); if (!TryDetermineTypeToGenerateIn( semanticDocument, ContainingType, SimpleNameOrMemberAccessExpression, cancellationToken, out var typeToGenerateIn, out var isStatic, out _)) { return false; } var semanticFacts = semanticDocument.Document.GetLanguageService<ISemanticFactsService>(); IsWrittenTo = semanticFacts.IsWrittenTo(semanticModel, InvocationExpressionOpt ?? SimpleNameOrMemberAccessExpression, cancellationToken); TypeToGenerateIn = typeToGenerateIn; IsStatic = isStatic; MethodGenerationKind = MethodGenerationKind.Member; return true; } private static ImmutableArray<string> GenerateParameterNamesBasedOnParameterTypes(ImmutableArray<IParameterSymbol> parameters) { using var _1 = ArrayBuilder<string>.GetInstance(out var names); using var _2 = ArrayBuilder<bool>.GetInstance(out var isFixed); foreach (var parameter in parameters) { var typeLocalName = parameter.Type.GetLocalName(fallback: parameter.Name); names.Add(new ParameterName(typeLocalName, isFixed: false).BestNameForParameter); isFixed.Add(false); } NameGenerator.EnsureUniquenessInPlace(names, isFixed); return names.ToImmutableAndClear(); } private static IMethodSymbol CreateMethodSymbolWithReturnType( ITypeSymbol expressionType) { return CodeGenerationSymbolFactory.CreateMethodSymbol( attributes: [], accessibility: default, modifiers: default, returnType: expressionType, refKind: RefKind.None, explicitInterfaceImplementations: default, name: null, typeParameters: [], parameters: []); } } }