-
Notifications
You must be signed in to change notification settings - Fork 229
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
S3900: Change parameter dereference check to top-down #7004
S3900: Change parameter dereference check to top-down #7004
Conversation
if (NullDereferenceCandidate(context.Operation.Instance) is { } candidate | ||
&& candidate.Kind == OperationKindEx.ParameterReference | ||
&& candidate.ToParameterReference() is var dereferencedParameter | ||
&& dereferencedParameter.Parameter is var parameter | ||
&& !parameter.Type.IsValueType | ||
&& IsParameterDereferenced(context.Operation) | ||
&& NullableStateIsNotKnownForParameter(parameter) | ||
&& !parameter.HasAttribute(KnownType.Microsoft_AspNetCore_Mvc_FromServicesAttribute)) | ||
{ | ||
var message = SemanticModel.GetDeclaredSymbol(Node).IsConstructor() | ||
? "Refactor this constructor to avoid using members of parameter '{0}' because it could be null." | ||
: "Refactor this method to add validation of parameter '{0}' before using it."; | ||
ReportIssue(operation, string.Format(message, operation.Syntax), context); | ||
ReportIssue(dereferencedParameter.WrappedOperation, string.Format(message, dereferencedParameter.WrappedOperation.Syntax), context); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would go with one of the following two:
if (NullDereferenceCandidate(context.Operation.Instance) is { } candidate
&& candidate.Kind == OperationKindEx.ParameterReference
&& candidate.ToParameterReference() is { Parameter: var parameter, WrappedOperation: var wrappedOperation }
&& !parameter.Type.IsValueType
&& NullableStateIsNotKnownForParameter(parameter)
&& !parameter.HasAttribute(KnownType.Microsoft_AspNetCore_Mvc_FromServicesAttribute))
{
var message = SemanticModel.GetDeclaredSymbol(Node).IsConstructor()
? "Refactor this constructor to avoid using members of parameter '{0}' because it could be null."
: "Refactor this method to add validation of parameter '{0}' before using it.";
ReportIssue(wrappedOperation, string.Format(message, wrappedOperation.Syntax), context);
}
if (NullDereferenceCandidate(context.Operation.Instance) is { } candidate
&& IsParameterReference(candidate, out var parameter, out var wrappedOperation)
&& !parameter.Type.IsValueType
&& NullableStateIsNotKnownForParameter(parameter)
&& !parameter.HasAttribute(KnownType.Microsoft_AspNetCore_Mvc_FromServicesAttribute))
{
var message = SemanticModel.GetDeclaredSymbol(Node).IsConstructor()
? "Refactor this constructor to avoid using members of parameter '{0}' because it could be null."
: "Refactor this method to add validation of parameter '{0}' before using it.";
ReportIssue(wrappedOperation, string.Format(message, wrappedOperation.Syntax), context);
}
cbd704f
to
ede37b4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. One more refactor suggestion. And you have to look into the ITs.
return candidate?.Kind == OperationKindEx.Conversion | ||
? ConversionOperand(candidate) | ||
: candidate; | ||
} | ||
|
||
private static IOperation ConversionOperand(IOperation operation) => | ||
operation.Kind == OperationKindEx.Conversion | ||
? ConversionOperand(operation.ToConversion().Operand) | ||
: operation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return candidate?.Kind == OperationKindEx.Conversion | |
? ConversionOperand(candidate) | |
: candidate; | |
} | |
private static IOperation ConversionOperand(IOperation operation) => | |
operation.Kind == OperationKindEx.Conversion | |
? ConversionOperand(operation.ToConversion().Operand) | |
: operation; | |
return UnwrapConversions(candidate); | |
} | |
private static IOperation UnwrapConversions(IOperation operation) => | |
operation?.Kind == OperationKindEx.Conversion | |
? UnwrapConversions(operation.ToConversion().Operand) | |
: operation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather keep the name ConversionOperand
, as it shows what the return value will be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue I see with that is that it will only return a conversion operand if the operation is a conversion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Taking shortcuts, will review implementaion in a minute
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code
...SonarAnalyzer.CSharp/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs
Show resolved
Hide resolved
...SonarAnalyzer.CSharp/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs
Show resolved
Hide resolved
...SonarAnalyzer.CSharp/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs
Outdated
Show resolved
Hide resolved
...SonarAnalyzer.CSharp/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs
Outdated
Show resolved
Hide resolved
...SonarAnalyzer.CSharp/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs
Outdated
Show resolved
Hide resolved
...SonarAnalyzer.CSharp/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs
Outdated
Show resolved
Hide resolved
"id": "S3900", | ||
"message": "Refactor this method to add validation of parameter 'constraint' before using it.", | ||
"location": { | ||
"uri": "sources\Nancy\src\Nancy\Routing\Constraints\ParameterizedRouteSegmentConstraintBase.cs", | ||
"region": { | ||
"startLine": 21, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually illustrates why top-to-bottom is better, due to the execution order
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -714,3 +714,26 @@ private class CustomClass | |||
public event EventHandler CustomEvent; | |||
} | |||
} | |||
|
|||
public class DereferencedMultipleTimesOnTheSameExecutionPath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice UTs!
OperationKindEx.ArrayElementReference => operation.ToArrayElementReference().ArrayReference, | ||
_ => null, | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This empty line doesn't help. If we would need semicolon, new line, newline as a command separator to make the language readable, we would have to look for another language.
d1fa962
to
d6211d9
Compare
1f33ea8
to
f587d85
Compare
f587d85
to
1426253
Compare
Kudos, SonarCloud Quality Gate passed! |
Kudos, SonarCloud Quality Gate passed! |
Minor update for S3900.
Switching from bottom-up search to top-down approach when dealing with operations.
This changed resolved a couple of FPs.