/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs
252 строки
10 KB
Cyrus Najmabadi
Remove unnecessary imports
10 мар 2025, 17:04
10 мар 2025, 17:04
58c992f
Код
Авторство
О чём код?
// 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; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.CSharp.Simplification; [ExportLanguageService(typeof(ISimplificationService), LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed partial class CSharpSimplificationService() : AbstractSimplificationService<CompilationUnitSyntax, ExpressionSyntax, StatementSyntax, CrefSyntax>(s_reducers) { // 1. Prefer 'var' simplification first. In other words we like `var v = (int)x` vs `int v = x` // 2. the cast simplifier should run earlier then everything else to minimize the type expressions // 3. Extension method reducer may insert parentheses. So run it before the parentheses remover. private static readonly ImmutableArray<AbstractReducer> s_reducers = [ new CSharpVarReducer(), new CSharpCastReducer(), new CSharpNameReducer(), new CSharpNullableAnnotationReducer(), new CSharpExtensionMethodReducer(), new CSharpParenthesizedExpressionReducer(), new CSharpParenthesizedPatternReducer(), new CSharpEscapingReducer(), new CSharpMiscellaneousReducer(), new CSharpInferredMemberNameReducer(), new CSharpDefaultExpressionReducer(), ]; public override SimplifierOptions DefaultOptions => CSharpSimplifierOptions.Default; public override SimplifierOptions GetSimplifierOptions(IOptionsReader options) => new CSharpSimplifierOptions(options); public override SyntaxNode Expand(SyntaxNode node, SemanticModel semanticModel, SyntaxAnnotation? annotationForReplacedAliasIdentifier, Func<SyntaxNode, bool>? expandInsideNode, bool expandParameter, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Simplifier_ExpandNode, cancellationToken)) { if (node is AttributeSyntax or AttributeArgumentSyntax or ConstructorInitializerSyntax or ExpressionSyntax or FieldDeclarationSyntax or StatementSyntax or CrefSyntax or XmlNameAttributeSyntax or TypeConstraintSyntax or BaseTypeSyntax) { var rewriter = new Expander(semanticModel, expandInsideNode, expandParameter, cancellationToken, annotationForReplacedAliasIdentifier); return rewriter.Visit(node); } else { throw new ArgumentException(CSharpWorkspaceResources.Only_attributes_constructor_initializers_expressions_or_statements_can_be_made_explicit, nameof(node)); } } } public override SyntaxToken Expand(SyntaxToken token, SemanticModel semanticModel, Func<SyntaxNode, bool>? expandInsideNode, CancellationToken cancellationToken) { Contract.ThrowIfNull(token.Parent); using (Logger.LogBlock(FunctionId.Simplifier_ExpandToken, cancellationToken)) { var rewriter = new Expander(semanticModel, expandInsideNode, false, cancellationToken); var rewrittenToken = TryEscapeIdentifierToken(rewriter.VisitToken(token), token.Parent).WithAdditionalAnnotations(Simplifier.Annotation); if (TryAddLeadingElasticTriviaIfNecessary(rewrittenToken, token, out var rewrittenTokenWithElasticTrivia)) { return rewrittenTokenWithElasticTrivia; } return rewrittenToken; } } public static SyntaxToken TryEscapeIdentifierToken(SyntaxToken syntaxToken, SyntaxNode parentOfToken) { // do not escape an already escaped identifier if (syntaxToken.IsVerbatimIdentifier()) { return syntaxToken; } if (SyntaxFacts.GetKeywordKind(syntaxToken.ValueText) == SyntaxKind.None && SyntaxFacts.GetContextualKeywordKind(syntaxToken.ValueText) == SyntaxKind.None) { return syntaxToken; } if (SyntaxFacts.GetContextualKeywordKind(syntaxToken.ValueText) == SyntaxKind.UnderscoreToken) { return syntaxToken; } var parent = parentOfToken.Parent; if (parentOfToken is SimpleNameSyntax && parent.IsKind(SyntaxKind.XmlNameAttribute)) { // do not try to escape XML name attributes return syntaxToken; } // do not escape global in a namespace qualified name if (parent.IsKind(SyntaxKind.AliasQualifiedName) && syntaxToken.ValueText == "global") { return syntaxToken; } // safe to escape identifier return syntaxToken.CopyAnnotationsTo( SyntaxFactory.VerbatimIdentifier( syntaxToken.LeadingTrivia, syntaxToken.ToString(), syntaxToken.ValueText, syntaxToken.TrailingTrivia)) .WithAdditionalAnnotations(Simplifier.Annotation); } public static T AppendElasticTriviaIfNecessary<T>(T rewrittenNode, T originalNode) where T : SyntaxNode { var firstRewrittenToken = rewrittenNode.GetFirstToken(true, false, true, true); var firstOriginalToken = originalNode.GetFirstToken(true, false, true, true); if (TryAddLeadingElasticTriviaIfNecessary(firstRewrittenToken, firstOriginalToken, out var rewrittenTokenWithLeadingElasticTrivia)) { return rewrittenNode.ReplaceToken(firstRewrittenToken, rewrittenTokenWithLeadingElasticTrivia); } return rewrittenNode; } private static bool TryAddLeadingElasticTriviaIfNecessary(SyntaxToken token, SyntaxToken originalToken, out SyntaxToken tokenWithLeadingWhitespace) { tokenWithLeadingWhitespace = default; if (token.HasLeadingTrivia) { return false; } var previousToken = originalToken.GetPreviousToken(); if (previousToken.HasTrailingTrivia) { return false; } tokenWithLeadingWhitespace = token.WithLeadingTrivia(SyntaxFactory.ElasticMarker).WithAdditionalAnnotations(Formatter.Annotation); return true; } protected override SemanticModel GetSpeculativeSemanticModel(ref SyntaxNode nodeToSpeculate, SemanticModel originalSemanticModel, SyntaxNode originalNode) { var syntaxNodeToSpeculate = nodeToSpeculate; Contract.ThrowIfNull(syntaxNodeToSpeculate); Contract.ThrowIfFalse(SpeculationAnalyzer.CanSpeculateOnNode(nodeToSpeculate)); return SpeculationAnalyzer.CreateSpeculativeSemanticModelForNode(originalNode, syntaxNodeToSpeculate, originalSemanticModel); } protected override ImmutableArray<NodeOrTokenToReduce> GetNodesAndTokensToReduce(SyntaxNode root, Func<SyntaxNodeOrToken, bool> isNodeOrTokenOutsideSimplifySpans) => NodesAndTokensToReduceComputer.Compute(root, isNodeOrTokenOutsideSimplifySpans); protected override bool NodeRequiresNonSpeculativeSemanticModel(SyntaxNode node) => false; private const string s_CS8019_UnusedUsingDirective = "CS8019"; protected override void GetUnusedNamespaceImports(SemanticModel model, HashSet<SyntaxNode> namespaceImports, CancellationToken cancellationToken) { var root = model.SyntaxTree.GetRoot(cancellationToken); var diagnostics = model.GetDiagnostics(cancellationToken: cancellationToken); foreach (var diagnostic in diagnostics) { if (diagnostic.Id == s_CS8019_UnusedUsingDirective) { if (root.FindNode(diagnostic.Location.SourceSpan) is UsingDirectiveSyntax node) { namespaceImports.Add(node); } } } } // Is the tuple on either side of a deconstruction (top-level or nested)? private static bool IsTupleInDeconstruction(SyntaxNode tuple) { Debug.Assert(tuple.IsKind(SyntaxKind.TupleExpression)); var currentTuple = tuple; do { var parent = currentTuple.Parent; if (parent.IsKind(SyntaxKind.SimpleAssignmentExpression)) { return true; } if (!parent.IsKind(SyntaxKind.Argument)) { return false; } var grandParent = parent.Parent; if (!grandParent.IsKind(SyntaxKind.TupleExpression)) { return false; } currentTuple = grandParent; } while (true); } protected override void AddImportDeclarations(CompilationUnitSyntax root, ArrayBuilder<SyntaxNode> importDeclarations) { importDeclarations.AddRange(root.Usings); foreach (var member in root.Members) { if (member is BaseNamespaceDeclarationSyntax baseNamespace) AddImportDeclarations(baseNamespace, importDeclarations); } static void AddImportDeclarations(BaseNamespaceDeclarationSyntax baseNamespace, ArrayBuilder<SyntaxNode> importDeclarations) { importDeclarations.AddRange(baseNamespace.Usings); foreach (var member in baseNamespace.Members) { if (member is BaseNamespaceDeclarationSyntax childNamespace) AddImportDeclarations(childNamespace, importDeclarations); } } } }