diff --git a/ChangeLog.md b/ChangeLog.md index 95341bee0b..2611d5673f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Fix analyzer [RCS1090](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1090) ([PR](https://github.com/dotnet/roslynator/pull/1566)) +- [CLI] Fix command `generate-doc` ([PR](https://github.com/dotnet/roslynator/pull/1568)) + ### Change - Update analyzer [RCS1077](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1077) ([PR](https://github.com/dotnet/roslynator/pull/1653)) diff --git a/src/Analyzers/CSharp/Analysis/ConfigureAwaitAnalyzer.cs b/src/Analyzers/CSharp/Analysis/ConfigureAwaitAnalyzer.cs index 7e0bbc50d2..b4e4cb57cc 100644 --- a/src/Analyzers/CSharp/Analysis/ConfigureAwaitAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/ConfigureAwaitAnalyzer.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -87,7 +88,7 @@ private static void RemoveCallToConfigureAwait(SyntaxNodeAnalysisContext context // ^^^^^^^^^^^^^^^^^^^^^^ SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(expression); - if (!IsConfigureAwait(invocationInfo)) + if (!IsConfigureAwaitFalse(invocationInfo, context.SemanticModel, context.CancellationToken)) return; ITypeSymbol awaitedType = context.SemanticModel.GetTypeSymbol(expression, context.CancellationToken); @@ -133,6 +134,13 @@ private static bool IsConfigureAwait(SimpleMemberInvocationExpressionInfo invoca && invocationInfo.Arguments.Count == 1; } + private static bool IsConfigureAwaitFalse(SimpleMemberInvocationExpressionInfo invocationInfo, SemanticModel semanticModel, CancellationToken cancellationToken) + { + return IsConfigureAwait(invocationInfo) + && semanticModel.GetConstantValue(invocationInfo.Arguments[0].Expression, cancellationToken).Value is bool boolValue + && !boolValue; + } + private static bool IsConfigureAwaitable(ITypeSymbol typeSymbol, SemanticModel semanticModel, int position) { return semanticModel.LookupSymbols(position, typeSymbol, "ConfigureAwait", includeReducedExtensionMethods: true) diff --git a/src/Documentation/TextUtility.cs b/src/Documentation/TextUtility.cs index dc38e122a7..ba0f0366eb 100644 --- a/src/Documentation/TextUtility.cs +++ b/src/Documentation/TextUtility.cs @@ -35,6 +35,7 @@ public static string RemoveLeadingTrailingNewLine( } if (trailingNewLine + && length > startIndex && s[length - 1] == '\n') { length--; diff --git a/src/Tests/Analyzers.Tests/RCS1090RemoveCallToConfigureAwaitTests.cs b/src/Tests/Analyzers.Tests/RCS1090RemoveCallToConfigureAwaitTests.cs index 1ad0d7240b..89060e0489 100644 --- a/src/Tests/Analyzers.Tests/RCS1090RemoveCallToConfigureAwaitTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1090RemoveCallToConfigureAwaitTests.cs @@ -553,6 +553,23 @@ public Awaitable ConfigureAwait(bool continueOnCapturedContext) return default(Awaitable); } } +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConfigureAwait)] + public async Task TestNoDiagnostic_ConfigureAwaitTrue() + { + await VerifyNoDiagnosticAsync(@" +using System.Threading.Tasks; + +class C +{ + async Task M() + { + Task task = default; + await task.ConfigureAwait(true); + } +} "); } }