/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Analyzers/Core/CodeFixes/AddParameter/AddParameterService.cs
237 строк
11 KB
Cyrus Najmabadi
Remove pointless xml doc comment tags
09 янв 2025, 22:23
09 янв 2025, 22:23
cdcfce1
Код
Авторство
О чём код?
// 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.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor; using Microsoft.CodeAnalysis.InitializeParameter; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddParameter; internal static class AddParameterService { private static readonly SyntaxAnnotation s_annotation = new(); /// <summary> /// Checks if there are indications that there might be more than one declarations that need to be fixed. /// The check does not look-up if there are other declarations (this is done later in the CodeAction). /// </summary> public static bool HasCascadingDeclarations(IMethodSymbol method) { // Don't cascade constructors if (method.IsConstructor()) { return false; } // Virtual methods of all kinds might have overrides somewhere else that need to be fixed. if (method.IsVirtual || method.IsOverride || method.IsAbstract) { return true; } // If interfaces are involved we will fix those too // Explicit interface implementations are easy to detect if (method.ExplicitInterfaceImplementations.Length > 0) { return true; } // For implicit interface implementations lets check if the characteristic of the method // allows it to implicit implement an interface member. if (method.DeclaredAccessibility != Accessibility.Public) { return false; } if (method.IsStatic) { return false; } // Now check if the method does implement an interface member if (method.ExplicitOrImplicitInterfaceImplementations().Length > 0) { return true; } return false; } /// <summary> /// Adds a parameter to a method. /// </summary> /// <param name="newParameterIndex"><see langword="null"/> to add as the final parameter</param> public static async Task<Solution> AddParameterAsync<TExpressionSyntax>( Document invocationDocument, IMethodSymbol method, ITypeSymbol newParameterType, RefKind refKind, ParameterName parameterName, Argument<TExpressionSyntax>? argument, int? newParameterIndex, bool fixAllReferences, CancellationToken cancellationToken) where TExpressionSyntax : SyntaxNode { var solution = invocationDocument.Project.Solution; var referencedSymbols = fixAllReferences ? await FindMethodDeclarationReferencesAsync(invocationDocument, method, cancellationToken).ConfigureAwait(false) : method.GetAllMethodSymbolsOfPartialParts(); var anySymbolReferencesNotInSource = referencedSymbols.Any(static symbol => !symbol.IsFromSource()); var locationsInSource = referencedSymbols.Where(symbol => symbol.IsFromSource()); // Indexing Locations[0] is valid because IMethodSymbols have one location at most // and IsFromSource() tests if there is at least one location. var locationsByDocument = locationsInSource.ToLookup( declarationLocation => solution.GetRequiredDocument(declarationLocation.Locations[0].SourceTree!)); foreach (var documentLookup in locationsByDocument) { var document = documentLookup.Key; // May not have syntax facts for a different language in CodeStyle layer. var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>(); if (syntaxFacts is null) continue; var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var editor = new SyntaxEditor(syntaxRoot, solution.Services); var generator = editor.Generator; foreach (var currentMethodToUpdate in documentLookup) { var methodNode = syntaxRoot.FindNode(currentMethodToUpdate.Locations[0].SourceSpan, getInnermostNodeForTie: true); var existingParameters = generator.GetParameters(methodNode); var insertionIndex = newParameterIndex ?? existingParameters.Count; // if the preceding parameter is optional, the new parameter must also be optional // see also BC30202 and CS1737 var parameterMustBeOptional = insertionIndex > 0 && syntaxFacts.GetDefaultOfParameter(existingParameters[insertionIndex - 1]) != null; var parameterSymbol = CreateParameterSymbol( currentMethodToUpdate, newParameterType, refKind, parameterMustBeOptional, parameterName.BestNameForParameter); var argumentInitializer = parameterMustBeOptional ? generator.DefaultExpression(newParameterType) : null; var parameterDeclaration = generator .ParameterDeclaration(parameterSymbol, argumentInitializer) .WithAdditionalAnnotations(Formatter.Annotation, s_annotation); if (anySymbolReferencesNotInSource && currentMethodToUpdate == method) { parameterDeclaration = parameterDeclaration.WithAdditionalAnnotations( ConflictAnnotation.Create(CodeFixesResources.Related_method_signatures_found_in_metadata_will_not_be_updated)); } if (method.MethodKind == MethodKind.ReducedExtension && insertionIndex < existingParameters.Count) insertionIndex++; AddParameterEditor.AddParameter( syntaxFacts, editor, methodNode, insertionIndex, parameterDeclaration, cancellationToken); } var newRoot = editor.GetChangedRoot(); solution = solution.WithDocumentSyntaxRoot(document.Id, newRoot); } // Now that we've added the parameter to the method, see if we added to a constructor that we then want to // assign that parameter to a field/property to as well. solution = await AddConstructorAssignmentsAsync(solution).ConfigureAwait(false); return solution; async Task<Solution> AddConstructorAssignmentsAsync(Solution rewrittenSolution) { var finalSolution = await TryAddConstructorAssignmentsAsync(rewrittenSolution).ConfigureAwait(false); return finalSolution ?? rewrittenSolution; } async Task<Solution?> TryAddConstructorAssignmentsAsync(Solution rewrittenSolution) { // If we weren't adding a parameter to a constructor, we have nothing to do here. if (method.MethodKind != MethodKind.Constructor) return null; // If we didn't have an argument indicating what was passed to the constructor, then we have nothing to do. if (argument is null) return null; // Only want to do this if we updated a single constructor in a single document. var documentsUpdated = locationsByDocument.Select(g => g.Key).ToSet(); if (documentsUpdated.Count != 1) return null; var documentId = documentsUpdated.Single().Id; // Now go find the constructor after the parameter was added to it. var rewrittenDocument = rewrittenSolution.GetRequiredDocument(documentId); var rewrittenSyntaxRoot = await rewrittenDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var parameterDeclaration = rewrittenSyntaxRoot.GetAnnotatedNodes(s_annotation).SingleOrDefault(); if (parameterDeclaration is null) return null; var semanticDocument = await SemanticDocument.CreateAsync(rewrittenDocument, cancellationToken).ConfigureAwait(false); if (semanticDocument.SemanticModel.GetDeclaredSymbol(parameterDeclaration, cancellationToken) is not IParameterSymbol parameter) return null; if (parameter.ContainingSymbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, DeclaringSyntaxReferences: [var reference] } constructor) return null; var (_, parameterToExistingMember, _, _) = await GenerateConstructorHelpers.GetParametersAsync( semanticDocument, constructor.ContainingType, [argument.Value], [parameter.Type], [new ParameterName(parameter.Name, isFixed: true)], cancellationToken).ConfigureAwait(false); var memberToAssignTo = parameterToExistingMember.FirstOrDefault().Value; if (memberToAssignTo is null) return null; var initializeParameterService = rewrittenDocument.GetRequiredLanguageService<IInitializeParameterService>(); return await initializeParameterService.AddAssignmentAsync( rewrittenDocument, parameter, memberToAssignTo, cancellationToken).ConfigureAwait(false); } } private static async Task<ImmutableArray<IMethodSymbol>> FindMethodDeclarationReferencesAsync( Document invocationDocument, IMethodSymbol method, CancellationToken cancellationToken) { var referencedSymbols = await SymbolFinder.FindReferencesAsync( method, invocationDocument.Project.Solution, cancellationToken).ConfigureAwait(false); return [.. referencedSymbols.Select(referencedSymbol => referencedSymbol.Definition) .OfType<IMethodSymbol>() .Distinct()]; } private static IParameterSymbol CreateParameterSymbol( IMethodSymbol method, ITypeSymbol parameterType, RefKind refKind, bool isOptional, string argumentNameSuggestion) { var uniqueName = NameGenerator.EnsureUniqueness(argumentNameSuggestion, method.Parameters.Select(p => p.Name)); var newParameterSymbol = CodeGenerationSymbolFactory.CreateParameterSymbol( attributes: default, refKind: refKind, isOptional: isOptional, isParams: false, type: parameterType, name: uniqueName); return newParameterSymbol; } }