提交 00cf9ee8 编写于 作者: M Manish Vasani

Oops forgot to checkin the files that were removed when moving couple of...

Oops forgot to checkin the files that were removed when moving couple of language services to shared layer
上级 5c8f280c
// 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.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.MoveDeclarationNearReference;
namespace Microsoft.CodeAnalysis.CSharp.MoveDeclarationNearReference
{
[ExportLanguageService(typeof(IMoveDeclarationNearReferenceService), LanguageNames.CSharp), Shared]
internal partial class CSharpMoveDeclarationNearReferenceService :
AbstractMoveDeclarationNearReferenceService<
CSharpMoveDeclarationNearReferenceService,
StatementSyntax,
LocalDeclarationStatementSyntax,
VariableDeclaratorSyntax>
{
[ImportingConstructor]
public CSharpMoveDeclarationNearReferenceService()
{
}
protected override bool IsMeaningfulBlock(SyntaxNode node)
{
return node is AnonymousFunctionExpressionSyntax ||
node is LocalFunctionStatementSyntax ||
node is CommonForEachStatementSyntax ||
node is ForStatementSyntax ||
node is WhileStatementSyntax ||
node is DoStatementSyntax ||
node is CheckedStatementSyntax;
}
protected override SyntaxNode GetVariableDeclaratorSymbolNode(VariableDeclaratorSyntax variableDeclarator)
=> variableDeclarator;
protected override bool IsValidVariableDeclarator(VariableDeclaratorSyntax variableDeclarator)
=> true;
protected override SyntaxToken GetIdentifierOfVariableDeclarator(VariableDeclaratorSyntax variableDeclarator)
=> variableDeclarator.Identifier;
protected override async Task<bool> TypesAreCompatibleAsync(
Document document, ILocalSymbol localSymbol,
LocalDeclarationStatementSyntax declarationStatement,
SyntaxNode right, CancellationToken cancellationToken)
{
var type = declarationStatement.Declaration.Type;
if (type.IsVar)
{
// Type inference. Only merge if types match.
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var rightType = semanticModel.GetTypeInfo(right, cancellationToken);
return Equals(localSymbol.Type, rightType.Type);
}
return true;
}
protected override bool CanMoveToBlock(ILocalSymbol localSymbol, SyntaxNode currentBlock, SyntaxNode destinationBlock)
=> localSymbol.CanSafelyMoveLocalToBlock(currentBlock, destinationBlock);
}
}
// 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.Composition;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.RemoveUnusedParametersAndValues;
using Microsoft.CodeAnalysis.ReplaceDiscardDeclarationsWithAssignments;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.ReplaceDiscardDeclarationsWithAssignments
{
[ExportLanguageService(typeof(IReplaceDiscardDeclarationsWithAssignmentsService), LanguageNames.CSharp), Shared]
internal sealed class CSharpReplaceDiscardDeclarationsWithAssignmentsService : IReplaceDiscardDeclarationsWithAssignmentsService
{
[ImportingConstructor]
public CSharpReplaceDiscardDeclarationsWithAssignmentsService()
{
}
public Task<SyntaxNode> ReplaceAsync(SyntaxNode memberDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
var editor = new SyntaxEditor(memberDeclaration, CSharpSyntaxGenerator.Instance);
foreach (var child in memberDeclaration.DescendantNodes())
{
switch (child)
{
case LocalDeclarationStatementSyntax localDeclarationStatement:
if (localDeclarationStatement.Declaration.Variables.Any(IsDiscardDeclaration))
{
RemoveDiscardHelper.ProcessDeclarationStatement(localDeclarationStatement, editor);
}
break;
case CatchDeclarationSyntax catchDeclaration:
if (IsDiscardDeclaration(catchDeclaration))
{
// "catch (Exception _)" => "catch (Exception)"
editor.ReplaceNode(catchDeclaration, catchDeclaration.WithIdentifier(default));
}
break;
case DeclarationExpressionSyntax declarationExpression:
if (declarationExpression.Designation is DiscardDesignationSyntax discardSyntax)
{
// "M(out var _)" => "M(out _)"
// "M(out int _)" => "M(out _)"
var discardToken = SyntaxFactory.Identifier(
leading: declarationExpression.GetLeadingTrivia(),
contextualKind: SyntaxKind.UnderscoreToken,
text: discardSyntax.UnderscoreToken.Text,
valueText: discardSyntax.UnderscoreToken.ValueText,
trailing: declarationExpression.GetTrailingTrivia());
var replacementNode = SyntaxFactory.IdentifierName(discardToken);
// Removing explicit type is possible only if there are no overloads of the method with same parameter.
// For example, if method "M" had overloads with signature "void M(int x)" and "void M(char x)",
// then the replacement "M(out int _)" => "M(out _)" will cause overload resolution error.
// Bail out if replacement changes semantics.
var speculationAnalyzer = new SpeculationAnalyzer(declarationExpression,
replacementNode, semanticModel, cancellationToken);
if (!speculationAnalyzer.ReplacementChangesSemantics())
{
editor.ReplaceNode(declarationExpression, replacementNode);
}
}
break;
case DeclarationPatternSyntax declarationPattern:
if (declarationPattern.Designation is DiscardDesignationSyntax discardDesignationSyntax &&
declarationPattern.Parent is IsPatternExpressionSyntax isPatternExpression)
{
// "x is int _" => "x is int"
var replacementNode = SyntaxFactory.BinaryExpression(
kind: SyntaxKind.IsExpression,
left: isPatternExpression.Expression,
operatorToken: isPatternExpression.IsKeyword,
right: declarationPattern.Type.WithTrailingTrivia(declarationPattern.GetTrailingTrivia()));
editor.ReplaceNode(isPatternExpression, replacementNode);
}
break;
}
}
return Task.FromResult(editor.GetChangedRoot());
}
private static bool IsDiscardDeclaration(VariableDeclaratorSyntax variable)
=> variable.Identifier.Text == AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.DiscardVariableName;
private static bool IsDiscardDeclaration(CatchDeclarationSyntax catchDeclaration)
=> catchDeclaration.Identifier.Text == AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.DiscardVariableName;
private sealed class RemoveDiscardHelper : IDisposable
{
private readonly LocalDeclarationStatementSyntax _localDeclarationStatement;
private readonly SyntaxEditor _editor;
private readonly ArrayBuilder<StatementSyntax> _statementsBuilder;
private SeparatedSyntaxList<VariableDeclaratorSyntax> _currentNonDiscardVariables;
private RemoveDiscardHelper(LocalDeclarationStatementSyntax localDeclarationStatement, SyntaxEditor editor)
{
_localDeclarationStatement = localDeclarationStatement;
_editor = editor;
_statementsBuilder = ArrayBuilder<StatementSyntax>.GetInstance();
_currentNonDiscardVariables = new SeparatedSyntaxList<VariableDeclaratorSyntax>();
}
public static void ProcessDeclarationStatement(
LocalDeclarationStatementSyntax localDeclarationStatement,
SyntaxEditor editor)
{
using var helper = new RemoveDiscardHelper(localDeclarationStatement, editor);
helper.ProcessDeclarationStatement();
}
public void Dispose() => _statementsBuilder.Free();
private void ProcessDeclarationStatement()
{
// We will replace all discard variable declarations in this method with discard assignments,
// For example,
// 1. "int _ = M();" is replaced with "_ = M();"
// 2. "int x = 1, _ = M(), y = 2;" is replaced with following statements:
// int x = 1;
// _ = M();
// int y = 2;
// This is done to prevent compiler errors where the existing method has a discard
// variable declaration at a line following the one we added a discard assignment in our fix.
// Process all the declared variables in the given local declaration statement,
// tracking the currently encountered non-discard variables.
foreach (var variable in _localDeclarationStatement.Declaration.Variables)
{
if (!IsDiscardDeclaration(variable))
{
// Add to the list of currently encountered non-discard variables
_currentNonDiscardVariables = _currentNonDiscardVariables.Add(variable);
}
else
{
// Process currently encountered non-discard variables to generate
// a local declaration statement with these variables.
GenerateDeclarationStatementForCurrentNonDiscardVariables();
// Process the discard variable declaration to replace it
// with an assignment to discard.
GenerateAssignmentForDiscardVariable(variable);
}
}
// Process all the remaining variable declarators to generate
// a local declaration statement with these variables.
GenerateDeclarationStatementForCurrentNonDiscardVariables();
// Now replace the original local declaration statement with
// the replacement statement list tracked in _statementsBuilder.
if (_statementsBuilder.Count == 0)
{
// Nothing to replace.
return;
}
// Move the leading trivia from original local declaration statement
// to the first statement of the replacement statement list.
var leadingTrivia = _localDeclarationStatement.Declaration.Type.GetLeadingTrivia()
.Concat(_localDeclarationStatement.Declaration.Type.GetTrailingTrivia());
_statementsBuilder[0] = _statementsBuilder[0].WithLeadingTrivia(leadingTrivia);
// Move the trailing trivia from original local declaration statement
// to the last statement of the replacement statement list.
var last = _statementsBuilder.Count - 1;
var trailingTrivia = _localDeclarationStatement.SemicolonToken.GetAllTrivia();
_statementsBuilder[last] = _statementsBuilder[last].WithTrailingTrivia(trailingTrivia);
// Replace the original local declaration statement with new statement list
// from _statementsBuilder.
if (_localDeclarationStatement.Parent is BlockSyntax || _localDeclarationStatement.Parent is SwitchSectionSyntax)
{
if (_statementsBuilder.Count > 1)
{
_editor.InsertAfter(_localDeclarationStatement, _statementsBuilder.Skip(1));
}
_editor.ReplaceNode(_localDeclarationStatement, _statementsBuilder[0]);
}
else
{
_editor.ReplaceNode(_localDeclarationStatement, SyntaxFactory.Block(_statementsBuilder));
}
}
private void GenerateDeclarationStatementForCurrentNonDiscardVariables()
{
// Generate a variable declaration with all the currently tracked non-discard declarators.
// For example, for a declaration "int x = 1, y = 2, _ = M(), z = 3;", we generate two variable declarations:
// 1. "int x = 1, y = 2;" and
// 2. "int z = 3;",
// which are split by a single assignment statement "_ = M();"
if (_currentNonDiscardVariables.Count > 0)
{
var statement = SyntaxFactory.LocalDeclarationStatement(
SyntaxFactory.VariableDeclaration(_localDeclarationStatement.Declaration.Type, _currentNonDiscardVariables))
.WithAdditionalAnnotations(Formatter.Annotation);
_statementsBuilder.Add(statement);
_currentNonDiscardVariables = new SeparatedSyntaxList<VariableDeclaratorSyntax>();
}
}
private void GenerateAssignmentForDiscardVariable(VariableDeclaratorSyntax variable)
{
Debug.Assert(IsDiscardDeclaration(variable));
// Convert a discard declaration with initializer of the form "int _ = M();" into
// a discard assignment "_ = M();"
if (variable.Initializer != null)
{
_statementsBuilder.Add(
SyntaxFactory.ExpressionStatement(
SyntaxFactory.AssignmentExpression(
kind: SyntaxKind.SimpleAssignmentExpression,
left: SyntaxFactory.IdentifierName(variable.Identifier),
operatorToken: variable.Initializer.EqualsToken,
right: variable.Initializer.Value))
.WithAdditionalAnnotations(Formatter.Annotation));
}
}
}
}
}
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.MoveDeclarationNearReference
{
internal partial class AbstractMoveDeclarationNearReferenceService<
TService,
TStatementSyntax,
TLocalDeclarationStatementSyntax,
TVariableDeclaratorSyntax>
{
private class State
{
public TLocalDeclarationStatementSyntax DeclarationStatement { get; private set; }
public TVariableDeclaratorSyntax VariableDeclarator { get; private set; }
public ILocalSymbol LocalSymbol { get; private set; }
public SyntaxNode OutermostBlock { get; private set; }
public SyntaxNode InnermostBlock { get; private set; }
public SyntaxList<TStatementSyntax> OutermostBlockStatements { get; private set; }
public SyntaxList<TStatementSyntax> InnermostBlockStatements { get; private set; }
public TStatementSyntax FirstStatementAffectedInInnermostBlock { get; private set; }
public int IndexOfDeclarationStatementInInnermostBlock { get; private set; }
public int IndexOfFirstStatementAffectedInInnermostBlock { get; private set; }
internal static async Task<State> GenerateAsync(
TService service,
Document document,
TLocalDeclarationStatementSyntax statement,
CancellationToken cancellationToken)
{
var state = new State();
if (!await state.TryInitializeAsync(service, document, statement, cancellationToken).ConfigureAwait(false))
{
return null;
}
return state;
}
private async Task<bool> TryInitializeAsync(
TService service,
Document document,
TLocalDeclarationStatementSyntax node,
CancellationToken cancellationToken)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
DeclarationStatement = node;
var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(DeclarationStatement);
if (variables.Count != 1)
{
return false;
}
VariableDeclarator = (TVariableDeclaratorSyntax)variables[0];
if (!service.IsValidVariableDeclarator(VariableDeclarator))
{
return false;
}
OutermostBlock = DeclarationStatement.Parent;
if (!syntaxFacts.IsExecutableBlock(OutermostBlock))
{
return false;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
LocalSymbol = (ILocalSymbol)semanticModel.GetDeclaredSymbol(
service.GetVariableDeclaratorSymbolNode(VariableDeclarator), cancellationToken);
if (LocalSymbol == null)
{
// This can happen in broken code, for example: "{ object x; object }"
return false;
}
var findReferencesResult = await SymbolFinder.FindReferencesAsync(LocalSymbol, document.Project.Solution, cancellationToken).ConfigureAwait(false);
var findReferencesList = findReferencesResult.ToList();
if (findReferencesList.Count != 1)
{
return false;
}
var references = findReferencesList[0].Locations.ToList();
if (references.Count == 0)
{
return false;
}
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var referencingStatements =
(from r in references
let token = syntaxRoot.FindToken(r.Location.SourceSpan.Start)
let statement = token.GetAncestor<TStatementSyntax>()
where statement != null
select statement).ToSet();
if (referencingStatements.Count == 0)
{
return false;
}
InnermostBlock = syntaxFacts.FindInnermostCommonExecutableBlock(referencingStatements);
if (InnermostBlock == null)
{
return false;
}
InnermostBlockStatements = syntaxFacts.GetExecutableBlockStatements(InnermostBlock);
OutermostBlockStatements = syntaxFacts.GetExecutableBlockStatements(OutermostBlock);
var allAffectedStatements = new HashSet<TStatementSyntax>(referencingStatements.SelectMany(
expr => expr.GetAncestorsOrThis<TStatementSyntax>()));
FirstStatementAffectedInInnermostBlock = InnermostBlockStatements.FirstOrDefault(allAffectedStatements.Contains);
if (FirstStatementAffectedInInnermostBlock == null)
{
return false;
}
if (FirstStatementAffectedInInnermostBlock == DeclarationStatement)
{
return false;
}
IndexOfDeclarationStatementInInnermostBlock = InnermostBlockStatements.IndexOf(DeclarationStatement);
IndexOfFirstStatementAffectedInInnermostBlock = InnermostBlockStatements.IndexOf(FirstStatementAffectedInInnermostBlock);
if (IndexOfDeclarationStatementInInnermostBlock >= 0 &&
IndexOfDeclarationStatementInInnermostBlock < IndexOfFirstStatementAffectedInInnermostBlock)
{
// Don't want to move a decl with initializer past other decls in order to move it to the first
// affected statement. If we do we can end up in the following situation:
#if false
int x = 0;
int y = 0;
Console.WriteLine(x + y);
#endif
// Each of these declarations will want to 'move' down to the WriteLine
// statement and we don't want to keep offering the refactoring. Note: this
// solution is overly aggressive. Technically if 'y' weren't referenced in
// Console.Writeline, then it might be a good idea to move the 'x'. But this
// gives good enough behavior most of the time.
// Note that if the variable declaration has no initializer, then we still want to offer
// the move as the closest reference will be an assignment to the variable
// and we should be able to merge the declaration and assignment into a single
// statement.
// So, we also check if the variable declaration has an initializer below.
if (syntaxFacts.GetInitializerOfVariableDeclarator(VariableDeclarator) != null &&
InDeclarationStatementGroup(IndexOfDeclarationStatementInInnermostBlock, IndexOfFirstStatementAffectedInInnermostBlock))
{
return false;
}
}
var previousToken = FirstStatementAffectedInInnermostBlock.GetFirstToken().GetPreviousToken();
var affectedSpan = TextSpan.FromBounds(previousToken.SpanStart, FirstStatementAffectedInInnermostBlock.Span.End);
if (semanticModel.SyntaxTree.OverlapsHiddenPosition(affectedSpan, cancellationToken))
{
return false;
}
return true;
}
private bool InDeclarationStatementGroup(
int originalIndexInBlock, int firstStatementIndexAffectedInBlock)
{
for (var i = originalIndexInBlock; i < firstStatementIndexAffectedInBlock; i++)
{
if (!(InnermostBlockStatements[i] is TLocalDeclarationStatementSyntax))
{
return false;
}
}
return true;
}
}
}
}
// 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.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
namespace Microsoft.CodeAnalysis.MoveDeclarationNearReference
{
internal abstract partial class AbstractMoveDeclarationNearReferenceService<
TService,
TStatementSyntax,
TLocalDeclarationStatementSyntax,
TVariableDeclaratorSyntax> : IMoveDeclarationNearReferenceService
where TService : AbstractMoveDeclarationNearReferenceService<TService, TStatementSyntax, TLocalDeclarationStatementSyntax, TVariableDeclaratorSyntax>
where TStatementSyntax : SyntaxNode
where TLocalDeclarationStatementSyntax : TStatementSyntax
where TVariableDeclaratorSyntax : SyntaxNode
{
protected abstract bool IsMeaningfulBlock(SyntaxNode node);
protected abstract bool CanMoveToBlock(ILocalSymbol localSymbol, SyntaxNode currentBlock, SyntaxNode destinationBlock);
protected abstract SyntaxNode GetVariableDeclaratorSymbolNode(TVariableDeclaratorSyntax variableDeclarator);
protected abstract bool IsValidVariableDeclarator(TVariableDeclaratorSyntax variableDeclarator);
protected abstract SyntaxToken GetIdentifierOfVariableDeclarator(TVariableDeclaratorSyntax variableDeclarator);
protected abstract Task<bool> TypesAreCompatibleAsync(Document document, ILocalSymbol localSymbol, TLocalDeclarationStatementSyntax declarationStatement, SyntaxNode right, CancellationToken cancellationToken);
public async Task<bool> CanMoveDeclarationNearReferenceAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
{
var state = await ComputeStateAsync(document, node, cancellationToken).ConfigureAwait(false);
return state != null;
}
private async Task<State> ComputeStateAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
{
if (!(node is TLocalDeclarationStatementSyntax statement))
{
return null;
}
var state = await State.GenerateAsync((TService)this, document, statement, cancellationToken).ConfigureAwait(false);
if (state == null)
{
return null;
}
if (state.IndexOfDeclarationStatementInInnermostBlock >= 0 &&
state.IndexOfDeclarationStatementInInnermostBlock == state.IndexOfFirstStatementAffectedInInnermostBlock - 1 &&
!await CanMergeDeclarationAndAssignmentAsync(document, state, cancellationToken).ConfigureAwait(false))
{
// Declaration statement is already closest to the first reference
// and they both cannot be merged into a single statement, so bail out.
return null;
}
if (!CanMoveToBlock(state.LocalSymbol, state.OutermostBlock, state.InnermostBlock))
{
return null;
}
return state;
}
public async Task<Document> MoveDeclarationNearReferenceAsync(
Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken)
{
var state = await ComputeStateAsync(document, localDeclarationStatement, cancellationToken).ConfigureAwait(false);
if (state == null)
{
return document;
}
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
var crossesMeaningfulBlock = CrossesMeaningfulBlock(state);
var warningAnnotation = crossesMeaningfulBlock
? WarningAnnotation.Create(FeaturesResources.Warning_colon_Declaration_changes_scope_and_may_change_meaning)
: null;
var canMergeDeclarationAndAssignment = await CanMergeDeclarationAndAssignmentAsync(document, state, cancellationToken).ConfigureAwait(false);
if (canMergeDeclarationAndAssignment)
{
editor.RemoveNode(state.DeclarationStatement);
MergeDeclarationAndAssignment(
document, state, editor, warningAnnotation);
}
else
{
var statementIndex = state.OutermostBlockStatements.IndexOf(state.DeclarationStatement);
if (statementIndex + 1 < state.OutermostBlockStatements.Count &&
state.OutermostBlockStatements[statementIndex + 1] == state.FirstStatementAffectedInInnermostBlock)
{
// Already at the correct location.
return document;
}
editor.RemoveNode(state.DeclarationStatement);
await MoveDeclarationToFirstReferenceAsync(
document, state, editor, warningAnnotation, cancellationToken).ConfigureAwait(false);
}
var newRoot = editor.GetChangedRoot();
return document.WithSyntaxRoot(newRoot);
}
private static async Task MoveDeclarationToFirstReferenceAsync(Document document, State state, SyntaxEditor editor, SyntaxAnnotation warningAnnotation, CancellationToken cancellationToken)
{
// If we're not merging with an existing declaration, make the declaration semantically
// explicit to improve the chances that it won't break code.
var explicitDeclarationStatement = await Simplifier.ExpandAsync(
state.DeclarationStatement, document, cancellationToken: cancellationToken).ConfigureAwait(false);
// place the declaration above the first statement that references it.
var declarationStatement = warningAnnotation == null
? explicitDeclarationStatement
: explicitDeclarationStatement.WithAdditionalAnnotations(warningAnnotation);
declarationStatement = declarationStatement.WithAdditionalAnnotations(Formatter.Annotation);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var newNextStatement = state.FirstStatementAffectedInInnermostBlock;
declarationStatement = declarationStatement.WithPrependedLeadingTrivia(
syntaxFacts.GetLeadingBlankLines(newNextStatement));
editor.InsertBefore(
state.FirstStatementAffectedInInnermostBlock,
declarationStatement);
editor.ReplaceNode(
newNextStatement,
newNextStatement.WithAdditionalAnnotations(Formatter.Annotation).WithLeadingTrivia(
syntaxFacts.GetTriviaAfterLeadingBlankLines(newNextStatement)));
// Move leading whitespace from the declaration statement to the next statement.
var statementIndex = state.OutermostBlockStatements.IndexOf(state.DeclarationStatement);
if (statementIndex + 1 < state.OutermostBlockStatements.Count)
{
var originalNextStatement = state.OutermostBlockStatements[statementIndex + 1];
editor.ReplaceNode(
originalNextStatement,
(current, generator) => current.WithAdditionalAnnotations(Formatter.Annotation).WithPrependedLeadingTrivia(
syntaxFacts.GetLeadingBlankLines(state.DeclarationStatement)));
}
}
private void MergeDeclarationAndAssignment(
Document document, State state, SyntaxEditor editor, SyntaxAnnotation warningAnnotation)
{
// Replace the first reference with a new declaration.
var declarationStatement = CreateMergedDeclarationStatement(document, state);
declarationStatement = warningAnnotation == null
? declarationStatement
: declarationStatement.WithAdditionalAnnotations(warningAnnotation);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
declarationStatement = declarationStatement.WithLeadingTrivia(
GetMergedTrivia(syntaxFacts, state.DeclarationStatement, state.FirstStatementAffectedInInnermostBlock));
editor.ReplaceNode(
state.FirstStatementAffectedInInnermostBlock,
declarationStatement.WithAdditionalAnnotations(Formatter.Annotation));
}
private ImmutableArray<SyntaxTrivia> GetMergedTrivia(
ISyntaxFactsService syntaxFacts, TStatementSyntax statement1, TStatementSyntax statement2)
{
return syntaxFacts.GetLeadingBlankLines(statement2).Concat(
syntaxFacts.GetTriviaAfterLeadingBlankLines(statement1)).Concat(
syntaxFacts.GetTriviaAfterLeadingBlankLines(statement2));
}
private bool CrossesMeaningfulBlock(State state)
{
var blocks = state.InnermostBlock.GetAncestorsOrThis<SyntaxNode>();
foreach (var block in blocks)
{
if (block == state.OutermostBlock)
{
break;
}
if (IsMeaningfulBlock(block))
{
return true;
}
}
return false;
}
private async Task<bool> CanMergeDeclarationAndAssignmentAsync(
Document document,
State state,
CancellationToken cancellationToken)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var initializer = syntaxFacts.GetInitializerOfVariableDeclarator(state.VariableDeclarator);
if (initializer == null ||
syntaxFacts.IsLiteralExpression(syntaxFacts.GetValueOfEqualsValueClause(initializer)))
{
var firstStatement = state.FirstStatementAffectedInInnermostBlock;
if (syntaxFacts.IsSimpleAssignmentStatement(firstStatement))
{
syntaxFacts.GetPartsOfAssignmentStatement(firstStatement, out var left, out var right);
if (syntaxFacts.IsIdentifierName(left))
{
var localSymbol = state.LocalSymbol;
var name = syntaxFacts.GetIdentifierOfSimpleName(left).ValueText;
if (syntaxFacts.StringComparer.Equals(name, localSymbol.Name))
{
return await TypesAreCompatibleAsync(
document, localSymbol, state.DeclarationStatement, right, cancellationToken).ConfigureAwait(false);
}
}
}
}
return false;
}
private TLocalDeclarationStatementSyntax CreateMergedDeclarationStatement(
Document document, State state)
{
var generator = SyntaxGenerator.GetGenerator(document);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
syntaxFacts.GetPartsOfAssignmentStatement(
state.FirstStatementAffectedInInnermostBlock,
out var left, out var operatorToken, out var right);
return state.DeclarationStatement.ReplaceNode(
state.VariableDeclarator,
generator.WithInitializer(
state.VariableDeclarator.WithoutTrailingTrivia(),
generator.EqualsValueClause(operatorToken, right))
.WithTrailingTrivia(state.VariableDeclarator.GetTrailingTrivia()));
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(FeaturesResources.Move_declaration_near_reference, createChangedDocument)
{
}
internal override CodeActionPriority Priority => CodeActionPriority.Low;
}
}
}
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.MoveDeclarationNearReference
{
internal interface IMoveDeclarationNearReferenceService : ILanguageService
{
/// <summary>
/// Returns true if <paramref name="localDeclarationStatement"/> is local declaration statement
/// that can be moved forward to be closer to its first reference.
/// </summary>
Task<bool> CanMoveDeclarationNearReferenceAsync(Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken);
/// <summary>
/// Moves <paramref name="localDeclarationStatement"/> closer to its first reference. Only
/// applicable if <see cref="CanMoveDeclarationNearReferenceAsync"/> returned
/// <code>true</code>. If not, then the original document will be returned unchanged.
/// </summary>
Task<Document> MoveDeclarationNearReferenceAsync(Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken);
}
}
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.ReplaceDiscardDeclarationsWithAssignments
{
internal interface IReplaceDiscardDeclarationsWithAssignmentsService : ILanguageService
{
/// <summary>
/// Returns an updated <paramref name="memberDeclaration"/> with all the
/// local declarations named '_' replaced with simple assignments to discard.
/// For example,
/// 1. <code>int _ = M();</code> is replaced with <code>_ = M();</code>
/// 2. <code>int x = 1, _ = M(), y = 2;</code> is replaced with following statements:
/// <code>
/// int x = 1;
/// _ = M();
/// int y = 2;
/// </code>
/// This is normally done in context of a code transformation that generates new discard assignment(s),
/// such as <code>_ = M();</code>, and wants to prevent compiler errors where the containing method already
/// has a discard variable declaration, say <code>var _ = M2();</code> at some line after the one
/// where the code transformation wants to generate new discard assignment(s), which would be a compiler error.
/// This method replaces such discard variable declarations with discard assignments.
/// </summary>
Task<SyntaxNode> ReplaceAsync(SyntaxNode memberDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken);
}
}
' 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.
Imports System.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.MoveDeclarationNearReference
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.MoveDeclarationNearReference
<ExportLanguageService(GetType(IMoveDeclarationNearReferenceService), LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicMoveDeclarationNearReferenceService
Inherits AbstractMoveDeclarationNearReferenceService(Of
VisualBasicMoveDeclarationNearReferenceService,
StatementSyntax,
LocalDeclarationStatementSyntax,
VariableDeclaratorSyntax)
<ImportingConstructor>
Public Sub New()
End Sub
Protected Overrides Function IsMeaningfulBlock(node As SyntaxNode) As Boolean
Return TypeOf node Is LambdaExpressionSyntax OrElse
TypeOf node Is ForOrForEachBlockSyntax OrElse
TypeOf node Is WhileStatementSyntax OrElse
TypeOf node Is DoStatementSyntax OrElse
TypeOf node Is LoopStatementSyntax
End Function
Protected Overrides Function GetVariableDeclaratorSymbolNode(variableDeclarator As VariableDeclaratorSyntax) As SyntaxNode
Return variableDeclarator.Names(0)
End Function
Protected Overrides Function IsValidVariableDeclarator(variableDeclarator As VariableDeclaratorSyntax) As Boolean
Return variableDeclarator.Names.Count = 1
End Function
Protected Overrides Function GetIdentifierOfVariableDeclarator(variableDeclarator As VariableDeclaratorSyntax) As SyntaxToken
Return variableDeclarator.Names(0).Identifier
End Function
Protected Overrides Function TypesAreCompatibleAsync(document As Document, localSymbol As ILocalSymbol, declarationStatement As LocalDeclarationStatementSyntax, right As SyntaxNode, cancellationToken As CancellationToken) As Task(Of Boolean)
Return SpecializedTasks.True
End Function
Protected Overrides Function CanMoveToBlock(localSymbol As ILocalSymbol, currentBlock As SyntaxNode, destinationBlock As SyntaxNode) As Boolean
Return True
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册