Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S2674: Code cleanup, refactoring, and performance improvements #5794

Merged
merged 8 commits into from
Jul 12, 2022

Conversation

martin-strecker-sonarsource
Copy link
Contributor

No description provided.

Copy link
Contributor Author

@martin-strecker-sonarsource martin-strecker-sonarsource left a comment

Choose a reason for hiding this comment

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

This rule is not affected by the sprint. The FN marked in the test file is compliant. I added some real FNs, but fixing those is out of the scope of the PR.

using (var stream = File.Open(fileName, FileMode.Open))
{
var result = new byte[stream.Length];
(int a, int b) = (stream.Read(result, 0, (int)stream.Length), 42); // FN
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was marked as FN but is actually compliant. I added some test cases with some real issues (with discards). I don't think, these should be addressed in this PR, though. I would say, that these cases are out of scope.

@@ -42,6 +42,16 @@ public static class ExpressionSyntaxExtensions
semanticModel.GetTypeInfo(expression).Type is { } expressionType
&& (expressionType.IsReferenceType || expressionType.Is(KnownType.System_Nullable_T));

public static ExpressionSyntax RemoveConditionalAccess(this ExpressionSyntax node)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was moved from CSharpSyntaxFacade.cs. No tests for this were added. Update: Tests were added.

Comment on lines +53 to +56
expression = expression.RemoveConditionalAccess();
if (expression is InvocationExpressionSyntax invocation
&& invocation.GetMethodCallIdentifier() is { } methodIdentifier
&& ReadMethodNames.Contains(methodIdentifier.Text, StringComparer.Ordinal)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Syntactic checks were added before the semantic model is queried. Test cases for different invocations were added.

Comment on lines +15 to +19
_ = stream.Read(result, 0, (int)stream.Length); // FN. The result is discarded
(_, var c) = (stream.Read(result, 0, (int)stream.Length), 42); // FN. The result is discarded
_ = stream.Read(result, 0, (int)stream.Length) is { }; // FN. The result is discarded
_ = stream.Read(result, 0, (int)stream.Length) is { } _; // FN. The result is discarded
_ = (stream.Read(result, 0, (int)stream.Length), 42) is (_, _); // FN. The result is discarded
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These are some coding patterns, where the return value is read, but discarded. To support these and other related patterns, flow analysis is needed IMO. We should document it on the Kanban board.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point! This would be an Improvement ticket for the backlog. We would need to have an overview of all rules which would need dataflow, but that is a different problem

Copy link
Contributor

Choose a reason for hiding this comment

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

@martin-strecker-sonarsource did you capture this somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not yet. There are three issues with this PR now:

  • Discards not handled properly
  • Upcoming ReadAtLeast.. methods not handled properly
  • RemoveConditionalAccess needs some companioning methods from roslyn (e.g. This one)

Where should we document these?

Copy link
Contributor

Choose a reason for hiding this comment

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

For Upcoming ReadAtLeast.. methods not handled properly -> you can add a comment to the C# 11 trello card

For RemoveConditionalAccess needs some companioning methods from roslyn (e.g. This one) - this can be a standalone GH issue

For Discards not handled properly - what does this mean? Does it generate FPs or FNs? This should be an "improvement" issue or an FP card in our GitHub backlog.

Copy link
Contributor

@andrei-epure-sonarsource andrei-epure-sonarsource left a comment

Choose a reason for hiding this comment

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

@martin-strecker-sonarsource do I understand well that this only adds tests? I am confused by the title of the PR

@martin-strecker-sonarsource
Copy link
Contributor Author

do I understand well that this only adds tests? I am confused by the title of the PR

That's right. See this comment why that's the case:

#5794 (review)

@martin-strecker-sonarsource martin-strecker-sonarsource changed the title S2674: Add support for deconstruction S2674: Code cleanup, refactoring, and performance improvements Jul 11, 2022
@martin-strecker-sonarsource
Copy link
Contributor Author

martin-strecker-sonarsource commented Jul 11, 2022

@andrei-epure-sonarsource This rule is about the Read and ReadAsync methods. There are new Read.. methods upcoming. See here: https://devblogs.microsoft.com/dotnet/announcing-dotnet-7-preview-5/#system-io-stream
Should I add support for the upcoming ReadAtLeast methods (ReadExactly does not apply) here or should we wait until .Net 7 is released?

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

91.9% 91.9% Coverage
0.0% 0.0% Duplication

@andrei-epure-sonarsource
Copy link
Contributor

Should I add support for the upcoming ReadAtLeast methods (ReadExactly does not apply) here or should we wait until .Net 7 is released?

@martin-strecker-sonarsource please wait, let's separate concerns and do a sprint focused on that.

Copy link
Contributor

@andrei-epure-sonarsource andrei-epure-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

[DataRow("A.B?.C.M()", ".C.M()")]
[DataRow("A.B?.C?.M()", ".M()")]
[DataRow("A.B?.C?.D", ".D")]
public void RemoveConditionalAccess_SimpleInvocation_CS(string invocation, string expected) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

[education] @martin-strecker-sonarsource what's the usefulness of this method, what's a scenario when this is useful?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ConditionalAccess messes up the syntax tree because of left/right associativity of the . operator vs. the ?. operator (I don't remember the details, but there is a 200+ thread on csharplang discussing the pros and cons). Do get an idea about the complexity involved you may want to have a look at:

https://github.com/dotnet/roslyn/blob/2aed89bfee5d5318cb62108be76365befb656c38/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs#L295-L371

This helper method simplifies any invocation to get just the "simple" part without the conditionals messing up the syntax tree. It is easier to get the Method symbol if the tree is cleaned up.

What we should actually do is to use GetParentConditionalAccessExpression of Roslyn, but I did not have looked whether this (and some other related helpers) are already present in our code base.

Copy link
Contributor

Choose a reason for hiding this comment

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

thanks!

Copy link
Contributor

@andrei-epure-sonarsource andrei-epure-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

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.

2 participants