Skip to content

New Rule S6966: Awaitable method should be used#9179

Merged
martin-strecker-sonarsource merged 11 commits intomasterfrom
revert-9178-revert-9101-Martin/S6966
Apr 24, 2024
Merged

New Rule S6966: Awaitable method should be used#9179
martin-strecker-sonarsource merged 11 commits intomasterfrom
revert-9178-revert-9101-Martin/S6966

Conversation

@martin-strecker-sonarsource
Copy link
Contributor

@martin-strecker-sonarsource martin-strecker-sonarsource commented Apr 22, 2024

Fixes #9096
Reverts #9178 which reverted #9101

FPs:
See also #9101 (comment) for FPs

The last FP is non-fixable.

@martin-strecker-sonarsource
Copy link
Contributor Author

martin-strecker-sonarsource commented Apr 23, 2024

Performance investigation based on https://docs.google.com/spreadsheets/d/1PcQ8NIEmelvtnyTmQ24wKk8T6LGkp7NZ_LDYiosWBT4/edit?usp=sharing

SimplCommerce

TPs: S6966: 96 instances

Analysis time for some rules
###
BackslashShouldBeAvoidedInAspNetRoutes:                   127
DeadStores:                                               628
DisposableNotDisposed:                                    566
FieldShouldBeReadonly:                                    220
InsecureEncryptionAlgorithm:                             1500
InvalidCastToInterface:                                  1597
SymbolicExecutionRunner:                                 2212
UseAwaitableMethod:                                       542

Statistics for UseAwaitableMethod:
Concurrent:                     True
Execution time (ms):            541,9652
Code Block Actions:             0
Code Block Start Actions:       46
Code Block End Actions:         0
Compilation Actions:            0
Compilation Start Actions:      46
Compilation End Actions:        0
Operation Actions:              0
Operation Block Actions:        0
Operation Block Start Actions:  0
Operation Block End Actions:    0
Semantic Model Actions:         0
Symbol Actions:                 0
Symbol Start Actions:           0
Symbol End Actions:             0
Syntax Node Actions:            0
Syntax Tree Actions:            0
Additional File Actions:        0
Suppression Actions:            0

Overall contribution:
image

The costs are mostly distributed between speculative binding (64%) and IsAwaitableNonDynamic (22%)
image

@martin-strecker-sonarsource
Copy link
Contributor Author

martin-strecker-sonarsource commented Apr 23, 2024

Northwind Traders

TPs: S6966: 3 instances

Performance results do not reflect what Peach measured.

Analysis time of selected rules
###
BackslashShouldBeAvoidedInAspNetRoutes:                    43
DeadStores:                                               562
DisposableNotDisposed:                                   1232
FieldShouldBeReadonly:                                    203
InsecureEncryptionAlgorithm:                              269
SymbolicExecutionRunner:                                  678
InvalidCastToInterface:                                   363
UseAwaitableMethod:                                        50

Statistics for UseAwaitableMethod:
Concurrent:                     True
Execution time (ms):            49,9416
Code Block Actions:             0
Code Block Start Actions:       8
Code Block End Actions:         0
Compilation Actions:            0
Compilation Start Actions:      8
Compilation End Actions:        0
Operation Actions:              0
Operation Block Actions:        0
Operation Block Start Actions:  0
Operation Block End Actions:    0
Semantic Model Actions:         0
Symbol Actions:                 0
Symbol Start Actions:           0
Symbol End Actions:             0
Syntax Node Actions:            0
Syntax Tree Actions:            0
Additional File Actions:        0
Suppression Actions:            0

80ms total contribution (less than 0.01% of overall runtime):
image

@martin-strecker-sonarsource
Copy link
Contributor Author

Aspnet Boilerplate

TPs: S6966: 129 instances

### Analyzistime of some rules
BackslashShouldBeAvoidedInAspNetRoutes:                   145
DeadStores:                                              2116
DisposableNotDisposed:                                    609
FieldShouldBeReadonly:                                    668
InsecureEncryptionAlgorithm:                             1784
InvalidCastToInterface:                                  2805
SymbolicExecutionRunner:                                 6983
UseAwaitableMethod:                                       520

Statistics for UseAwaitableMethod:
Concurrent:                     True
Execution time (ms):            520,1448
Code Block Actions:             0
Code Block Start Actions:       87
Code Block End Actions:         0
Compilation Actions:            0
Compilation Start Actions:      87
Compilation End Actions:        0
Operation Actions:              0
Operation Block Actions:        0
Operation Block Start Actions:  0
Operation Block End Actions:    0
Semantic Model Actions:         0
Symbol Actions:                 0
Symbol Start Actions:           0
Symbol End Actions:             0
Syntax Node Actions:            0
Syntax Tree Actions:            0
Additional File Actions:        0
Suppression Actions:            0

Overall (0.03%)
image

In the rule, the main driver are the same as above (speculative binding and IsAwaitableNonDynamic)
image

@martin-strecker-sonarsource
Copy link
Contributor Author

martin-strecker-sonarsource commented Apr 23, 2024

Performance gain by b42423c

SimplCommerce:
Before
image
After
image

The IsAwaitableNonDynamic perf issue is gone.

@martin-strecker-sonarsource
Copy link
Contributor Author

martin-strecker-sonarsource commented Apr 23, 2024

Performance improvement by 8047ff0

SimpleCommerce
Reduction of the number of speculative binds: 201 down from 464. Time spend is cut in half.
Before
image
After
image

@martin-strecker-sonarsource
Copy link
Contributor Author

Results after the two improvements:

Project Before After
SimpleComerce 542 294
AspNetBoilerplate 520 245

@martin-strecker-sonarsource martin-strecker-sonarsource marked this pull request as ready for review April 23, 2024 13:29
@martin-strecker-sonarsource martin-strecker-sonarsource force-pushed the revert-9178-revert-9101-Martin/S6966 branch from e2d5008 to 8047ff0 Compare April 24, 2024 08:50
Copy link
Contributor

@zsolt-kolbay-sonarsource zsolt-kolbay-sonarsource left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some polishing comments

var wellKnownExtensionMethodContainer = new WellKnownExtensionMethodContainer();
var queryable = compilation.GetTypeByMetadataName(KnownType.System_Linq_Queryable);
var enumerable = compilation.GetTypeByMetadataName(KnownType.System_Linq_Enumerable);
if (queryable is not null && enumerable is not null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check seems unnecessary. These types are available in all supported versions of .NET.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better safe than sorry. GetTypeByMetadataName may return null.

InvocationExpressionSyntax invocationExpression, SemanticModel semanticModel, ISymbol containingSymbol, CancellationToken cancel)
{
var awaitableRoot = GetAwaitableRootOfInvocation(invocationExpression);
if (awaitableRoot is { Parent: AwaitExpressionSyntax })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two if statements can be merged, they return the same value.

using System.Linq;
using System.Threading.Tasks;

public class C
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another test case to consider: when the async method has the same parameter + a CancellationToken.

void Foo(int arg) { }
Task FooAsync(int arg, CancellationToken token) => Task.CompletedTask;
...
async Task Bar()
{
    Foo(); // Noncompliant
}

{
return ImmutableArray<ISymbol>.Empty; // Not in an async scope
}
if (semanticModel.GetSymbolInfo(invocationExpression, cancel).Symbol is IMethodSymbol { MethodKind: not MethodKind.DelegateInvoke } methodSymbol
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be cached with a ConcurrentDictionary<IMethodSymbol, ImmutableArray<ISymbol>> to avoid further calls if the method was checked by the analyzer before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, this cache would bring little benefit based on the current performance measures.

Copy link
Contributor

@zsolt-kolbay-sonarsource zsolt-kolbay-sonarsource left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@martin-strecker-sonarsource martin-strecker-sonarsource force-pushed the revert-9178-revert-9101-Martin/S6966 branch from a18bbba to ec32e36 Compare April 24, 2024 13:47
@martin-strecker-sonarsource
Copy link
Contributor Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@sonarqubecloud
Copy link

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
No data about Duplication

See analysis details on SonarCloud

@sonarqubecloud
Copy link

@martin-strecker-sonarsource martin-strecker-sonarsource deleted the revert-9178-revert-9101-Martin/S6966 branch April 24, 2024 14:14
@martin-strecker-sonarsource
Copy link
Contributor Author

martin-strecker-sonarsource commented Apr 29, 2024

Peach validations

FPs

Statistics:
888 issues (634 cs and 254 cshtml) in 40 different projects found.

Project .cs .cshtml Grand Total
cloudscribe 35 221 256
orchard-core 107 9 116
simplcommerce 93 11 104
dotnet-runtime 84   84
wcf 38   38
bitwarden-core 31 4 35
ravendb 33   33
nuget-gallery 30   30
screentogif 22   22
jellyfin 16   16
embedio 16   16
protoactor-dotnet 15   15
aspnet-boilerplate 12 2 14
das-apim-endpoints 11   11
piranha-core 6 4 10
masstransit 9   9
awssdk 8   8
mudblazor 7   7
odata 6   6
obsidian 6   6
beyourmarket 6   6
smartstore 5   5
piranha-core-templates 3 2 5
orchard-cms 5   5
oqtane-framewok 4   4
umbraco 3   3
stylecop 3   3
omnisharp-roslyn 3   3
eventstore 3   3
egroo 3   3
northwind-traders 2   2
elasticsearch-net 2   2
up-blazor 1   1
shadowsocks-windows 1   1
PowerShellEditorServices 1   1
luthetus-ide 1   1
gittrends 1   1
dnn 1   1
CSharpLatest   1 1
akka 1   1
Grand Total 634 254 888

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New Rule S6966: Awaitable method should be used

2 participants