Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add analyzer "Dispose resource asynchronously" ([RCS1261](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1261)) ([PR](https://github.com/dotnet/roslynator/pull/1285)).

### Changed

- Improve refactoring 'Remove comment' [RR0098](https://josefpihrt.github.io/docs/roslynator/refactorings/RR0098) ([PR](https://github.com/dotnet/roslynator/pull/1284))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslynator.CodeFixes;
using Roslynator.CSharp.SyntaxRewriters;

namespace Roslynator.CSharp.CodeFixes;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DisposeResourceAsynchronouslyCodeFixProvider))]
[Shared]
public sealed class DisposeResourceAsynchronouslyCodeFixProvider : BaseCodeFixProvider
{
private const string Title = "Dispose resource asynchronously";

public override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(DiagnosticIdentifiers.DisposeResourceAsynchronously); }
}

public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

if (!TryFindFirstAncestorOrSelf(
root,
context.Span,
out SyntaxNode node,
predicate: f => f.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.UsingStatement)))
{
return;
}

Diagnostic diagnostic = context.Diagnostics[0];
Document document = context.Document;

switch (diagnostic.Id)
{
case DiagnosticIdentifiers.DisposeResourceAsynchronously:
{
if (node is LocalDeclarationStatementSyntax localDeclaration)
{
CodeAction codeAction = CodeAction.Create(
Title,
ct => RefactorAsync(document, localDeclaration, ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
else if (node is UsingStatementSyntax usingStatement)
{
CodeAction codeAction = CodeAction.Create(
Title,
ct => RefactorAsync(document, usingStatement, ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
else
{
throw new InvalidOperationException();
}

break;
}
}
}

private static async Task<Document> RefactorAsync(
Document document,
UsingStatementSyntax usingStatement,
CancellationToken cancellationToken)
{
UsingStatementSyntax newUsingStatement = usingStatement.WithAwaitKeyword(SyntaxFactory.Token(SyntaxKind.AwaitKeyword));

SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

return await RefactorAsync(document, usingStatement, newUsingStatement, semanticModel, cancellationToken).ConfigureAwait(false);
}

private static async Task<Document> RefactorAsync(
Document document,
LocalDeclarationStatementSyntax localDeclaration,
CancellationToken cancellationToken)
{
LocalDeclarationStatementSyntax newLocalDeclaration = localDeclaration.WithAwaitKeyword(SyntaxFactory.Token(SyntaxKind.AwaitKeyword));

SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

return await RefactorAsync(document, localDeclaration, newLocalDeclaration, semanticModel, cancellationToken).ConfigureAwait(false);
}

private static async Task<Document> RefactorAsync(
Document document,
StatementSyntax statement,
StatementSyntax newStatement,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
for (SyntaxNode node = statement.Parent; node is not null; node = node.Parent)
{
if (node is MethodDeclarationSyntax methodDeclaration)
{
if (methodDeclaration.Modifiers.Contains(SyntaxKind.AsyncKeyword))
return await document.ReplaceNodeAsync(statement, newStatement, cancellationToken).ConfigureAwait(false);

MethodDeclarationSyntax newNode = methodDeclaration.ReplaceNode(statement, newStatement);

IMethodSymbol methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken);

UseAsyncAwaitRewriter rewriter = UseAsyncAwaitRewriter.Create(methodSymbol);
var newBody = (BlockSyntax)rewriter.VisitBlock(newNode.Body);

newNode = newNode
.WithBody(newBody)
.InsertModifier(SyntaxKind.AsyncKeyword);

return await document.ReplaceNodeAsync(methodDeclaration, newNode, cancellationToken).ConfigureAwait(false);
}
else if (node is LocalFunctionStatementSyntax localFunction)
{
if (localFunction.Modifiers.Contains(SyntaxKind.AsyncKeyword))
return await document.ReplaceNodeAsync(statement, newStatement, cancellationToken).ConfigureAwait(false);

LocalFunctionStatementSyntax newNode = localFunction.ReplaceNode(statement, newStatement);

IMethodSymbol methodSymbol = semanticModel.GetDeclaredSymbol(localFunction, cancellationToken);

UseAsyncAwaitRewriter rewriter = UseAsyncAwaitRewriter.Create(methodSymbol);
var newBody = (BlockSyntax)rewriter.VisitBlock(newNode.Body);

newNode = newNode
.WithBody(newBody)
.InsertModifier(SyntaxKind.AsyncKeyword);

return await document.ReplaceNodeAsync(localFunction, newNode, cancellationToken).ConfigureAwait(false);
}
else if (node is LambdaExpressionSyntax lambdaExpression)
{
if (lambdaExpression.Modifiers.Contains(SyntaxKind.AsyncKeyword))
return await document.ReplaceNodeAsync(statement, newStatement, cancellationToken).ConfigureAwait(false);

LambdaExpressionSyntax newNode = lambdaExpression.ReplaceNode(statement, newStatement);

var methodSymbol = (IMethodSymbol)semanticModel.GetSymbol(lambdaExpression, cancellationToken);

UseAsyncAwaitRewriter rewriter = UseAsyncAwaitRewriter.Create(methodSymbol);
var newBody = (BlockSyntax)rewriter.VisitBlock((BlockSyntax)newNode.Body);

newNode = newNode
.WithBody(newBody)
.InsertModifier(SyntaxKind.AsyncKeyword);

return await document.ReplaceNodeAsync(lambdaExpression, newNode, cancellationToken).ConfigureAwait(false);
}
else if (node is AnonymousMethodExpressionSyntax anonymousMethod)
{
if (anonymousMethod.Modifiers.Contains(SyntaxKind.AsyncKeyword))
return await document.ReplaceNodeAsync(statement, newStatement, cancellationToken).ConfigureAwait(false);

AnonymousMethodExpressionSyntax newNode = anonymousMethod.ReplaceNode(statement, newStatement);

var methodSymbol = (IMethodSymbol)semanticModel.GetSymbol(anonymousMethod, cancellationToken);

UseAsyncAwaitRewriter rewriter = UseAsyncAwaitRewriter.Create(methodSymbol);
var newBody = (BlockSyntax)rewriter.VisitBlock((BlockSyntax)newNode.Body);

newNode = newNode
.WithBody(newBody)
.InsertModifier(SyntaxKind.AsyncKeyword);

return await document.ReplaceNodeAsync(anonymousMethod, newNode, cancellationToken).ConfigureAwait(false);
}
}

throw new InvalidOperationException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
if (!TryFindFirstAncestorOrSelf(root, context.Span, out LocalDeclarationStatementSyntax localDeclaration))
return;

foreach (Diagnostic diagnostic in context.Diagnostics)
Diagnostic diagnostic = context.Diagnostics[0];
Document document = context.Document;

switch (diagnostic.Id)
{
switch (diagnostic.Id)
{
case DiagnosticIdentifiers.InlineLocalVariable:
{
CodeAction codeAction = CodeAction.Create(
"Inline local variable",
ct => RefactorAsync(context.Document, localDeclaration, ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
break;
}
}
case DiagnosticIdentifiers.InlineLocalVariable:
{
CodeAction codeAction = CodeAction.Create(
"Inline local variable",
ct => RefactorAsync(document, localDeclaration, ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
break;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,13 @@
using Roslynator.CodeFixes;
using Roslynator.CSharp.SyntaxRewriters;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Roslynator.CSharp.CSharpFactory;

namespace Roslynator.CSharp.CodeFixes;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseAsyncAwaitCodeFixProvider))]
[Shared]
public sealed class UseAsyncAwaitCodeFixProvider : BaseCodeFixProvider
{
private static readonly SyntaxAnnotation[] _asyncAwaitAnnotation = new[] { new SyntaxAnnotation() };

private static readonly SyntaxAnnotation[] _asyncAwaitAnnotationAndFormatterAnnotation = new SyntaxAnnotation[] { _asyncAwaitAnnotation[0], Formatter.Annotation };

public override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(DiagnosticIdentifiers.UseAsyncAwait); }
Expand Down Expand Up @@ -140,93 +135,4 @@ private static async Task<Document> RefactorAsync(

throw new InvalidOperationException();
}

private class UseAsyncAwaitRewriter : SkipFunctionRewriter
{
private UseAsyncAwaitRewriter(bool keepReturnStatement)
{
KeepReturnStatement = keepReturnStatement;
}

public bool KeepReturnStatement { get; }

public static UseAsyncAwaitRewriter Create(IMethodSymbol methodSymbol)
{
ITypeSymbol returnType = methodSymbol.ReturnType.OriginalDefinition;

var keepReturnStatement = false;

if (returnType.EqualsOrInheritsFrom(MetadataNames.System_Threading_Tasks_ValueTask_T)
|| returnType.EqualsOrInheritsFrom(MetadataNames.System_Threading_Tasks_Task_T))
{
keepReturnStatement = true;
}

return new UseAsyncAwaitRewriter(keepReturnStatement: keepReturnStatement);
}

public override SyntaxNode VisitReturnStatement(ReturnStatementSyntax node)
{
ExpressionSyntax expression = node.Expression;

if (expression?.IsKind(SyntaxKind.AwaitExpression) == false)
{
if (KeepReturnStatement)
{
return node.WithExpression(AwaitExpression(expression.WithoutTrivia().Parenthesize()).WithTriviaFrom(expression));
}
else
{
return ExpressionStatement(AwaitExpression(expression.WithoutTrivia().Parenthesize()).WithTriviaFrom(expression))
.WithLeadingTrivia(node.GetLeadingTrivia())
.WithAdditionalAnnotations(_asyncAwaitAnnotationAndFormatterAnnotation);
}
}

return base.VisitReturnStatement(node);
}

public override SyntaxNode VisitBlock(BlockSyntax node)
{
node = (BlockSyntax)base.VisitBlock(node);

SyntaxList<StatementSyntax> statements = node.Statements;

statements = RewriteStatements(statements);

return node.WithStatements(statements);
}

public override SyntaxNode VisitSwitchSection(SwitchSectionSyntax node)
{
node = (SwitchSectionSyntax)base.VisitSwitchSection(node);

SyntaxList<StatementSyntax> statements = node.Statements;

statements = RewriteStatements(statements);

return node.WithStatements(statements);
}

private static SyntaxList<StatementSyntax> RewriteStatements(SyntaxList<StatementSyntax> statements)
{
for (int i = statements.Count - 1; i >= 0; i--)
{
StatementSyntax statement = statements[i];

if (statement.HasAnnotation(_asyncAwaitAnnotation[0]))
{
statements = statements.Replace(
statement,
statement.WithoutAnnotations(_asyncAwaitAnnotation).WithTrailingTrivia(NewLine()));

statements = statements.Insert(
i + 1,
ReturnStatement().WithTrailingTrivia(statement.GetTrailingTrivia()).WithFormatterAnnotation());
}
}

return statements;
}
}
}
Loading