diff --git a/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs index 73942cac671f7a011afb19090ea1e31eee20cda5..725cce127772f75f8ddb8dd5878f79c72548fa05 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs @@ -43,7 +43,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var declaration = GetDeclaration(root, textSpan); + var declaration = await GetDeclarationAsync(document, textSpan, cancellationToken).ConfigureAwait(false); if (declaration == null) { return; @@ -85,11 +85,45 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte c => UpdateDocumentAsync(document, declaredType, c))); } - private static SyntaxNode GetDeclaration(SyntaxNode root, TextSpan textSpan) + private static async Task GetDeclarationAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken) { - var token = root.FindToken(textSpan.Start); - return token.Parent?.FirstAncestorOrSelf( - a => a.IsKind(SyntaxKind.DeclarationExpression, SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement)); + // We want to provide refactoring for changing the Type of newly introduced variables in following cases: + // - DeclarationExpressionSyntax: `"42".TryParseInt32(out var number)` + // - VariableDeclarationSyntax: General field / variable declaration statement `var number = 42` + // - ForEachStatementSyntax: The variable that gets introduced by foreach `foreach(var number in numbers)` + // + // In addition to providing the refactoring when the whole node (i.e. the node that introduces the new variable) in question is selected + // we also want to enable it when only the type node is selected because this refactoring changes the type. We still have to make sure + // we're only working on TypeNodes for in above-mentioned situations. + + var refactoringHelperService = document.GetLanguageService(); + + var declNode = await refactoringHelperService.TryGetSelectedNodeAsync(document, textSpan, cancellationToken).ConfigureAwait(false); + if (declNode != null) + { + return declNode; + } + + var variableNode = await refactoringHelperService.TryGetSelectedNodeAsync(document, textSpan, cancellationToken).ConfigureAwait(false); + if (variableNode != null) + { + return variableNode; + } + + var foreachStatement = await refactoringHelperService.TryGetSelectedNodeAsync(document, textSpan, cancellationToken).ConfigureAwait(false); + if (foreachStatement != null) + { + return foreachStatement; + } + + var typeNode = await refactoringHelperService.TryGetSelectedNodeAsync(document, textSpan, cancellationToken).ConfigureAwait(false); + var typeNodeParent = typeNode?.Parent; + if (typeNodeParent != null && typeNodeParent.IsKind(SyntaxKind.DeclarationExpression, SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement)) + { + return typeNodeParent; + } + + return null; } private async Task UpdateDocumentAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)