Skip to content

Commit

Permalink
Fix S1125 FN: recognize "is" and "is not" keyword with constant patte…
Browse files Browse the repository at this point in the history
…rn (#7687)
  • Loading branch information
cristian-ambrosini-sonarsource committed Aug 4, 2023
1 parent 85c2441 commit 8f6674f
Show file tree
Hide file tree
Showing 20 changed files with 342 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Ember-MM/Ember%20Media%20Manager/dlgManualEdit.vb#L259",
"region": {
"startLine": 259,
"startColumn": 24,
"startColumn": 22,
"endLine": 259,
"endColumn": 29
}
Expand All @@ -72,7 +72,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Ember-MM/Ember%20Media%20Manager/dlgManualEdit.vb#L376",
"region": {
"startLine": 376,
"startColumn": 22,
"startColumn": 20,
"endLine": 376,
"endColumn": 27
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Ember-MM/EmberAPI/clsAPIDatabase.vb#L112",
"region": {
"startLine": 112,
"startColumn": 64,
"startColumn": 62,
"endLine": 112,
"endColumn": 69
}
Expand Down
8 changes: 4 additions & 4 deletions analyzers/its/expected/Nancy/Nancy--net452-S1125.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Nancy/src/Nancy/TinyIoc/TinyIoC.cs#L3289",
"region": {
"startLine": 3289,
"startColumn": 76,
"startColumn": 73,
"endLine": 3289,
"endColumn": 81
}
Expand Down Expand Up @@ -111,7 +111,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Nancy/src/Nancy/ViewEngines/SuperSimpleViewEngine/SuperSimpleViewEngine.cs#L284",
"region": {
"startLine": 284,
"startColumn": 44,
"startColumn": 41,
"endLine": 284,
"endColumn": 49
}
Expand All @@ -124,7 +124,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Nancy/src/Nancy/ViewEngines/SuperSimpleViewEngine/SuperSimpleViewEngine.cs#L306",
"region": {
"startLine": 306,
"startColumn": 45,
"startColumn": 42,
"endLine": 306,
"endColumn": 50
}
Expand All @@ -137,7 +137,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Nancy/src/Nancy/ViewEngines/SuperSimpleViewEngine/SuperSimpleViewEngine.cs#L487",
"region": {
"startLine": 487,
"startColumn": 53,
"startColumn": 50,
"endLine": 487,
"endColumn": 58
}
Expand Down
8 changes: 4 additions & 4 deletions analyzers/its/expected/Nancy/Nancy--netstandard2.0-S1125.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Nancy/src/Nancy/TinyIoc/TinyIoC.cs#L3289",
"region": {
"startLine": 3289,
"startColumn": 76,
"startColumn": 73,
"endLine": 3289,
"endColumn": 81
}
Expand Down Expand Up @@ -111,7 +111,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Nancy/src/Nancy/ViewEngines/SuperSimpleViewEngine/SuperSimpleViewEngine.cs#L284",
"region": {
"startLine": 284,
"startColumn": 44,
"startColumn": 41,
"endLine": 284,
"endColumn": 49
}
Expand All @@ -124,7 +124,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Nancy/src/Nancy/ViewEngines/SuperSimpleViewEngine/SuperSimpleViewEngine.cs#L306",
"region": {
"startLine": 306,
"startColumn": 45,
"startColumn": 42,
"endLine": 306,
"endColumn": 50
}
Expand All @@ -137,7 +137,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/Nancy/src/Nancy/ViewEngines/SuperSimpleViewEngine/SuperSimpleViewEngine.cs#L487",
"region": {
"startLine": 487,
"startColumn": 53,
"startColumn": 50,
"endLine": 487,
"endColumn": 58
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/akka.net/src/core/Akka/Configuration/Config.cs#L445",
"region": {
"startLine": 445,
"startColumn": 36,
"startColumn": 33,
"endLine": 445,
"endColumn": 41
}
Expand All @@ -20,7 +20,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/akka.net/src/core/Akka/Dispatch/Mailbox.cs#L385",
"region": {
"startLine": 385,
"startColumn": 80,
"startColumn": 77,
"endLine": 385,
"endColumn": 85
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/akka.net/src/core/Akka.Cluster/ClusterHeartbeat.cs#L663",
"region": {
"startLine": 663,
"startColumn": 48,
"startColumn": 45,
"endLine": 663,
"endColumn": 53
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/akka.net/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/ConsoleMessageSinkActor.cs#L54",
"region": {
"startLine": 54,
"startColumn": 57,
"startColumn": 54,
"endLine": 54,
"endColumn": 62
}
Expand All @@ -20,7 +20,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/akka.net/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/ConsoleMessageSinkActor.cs#L59",
"region": {
"startLine": 59,
"startColumn": 71,
"startColumn": 68,
"endLine": 59,
"endColumn": 76
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/akka.net/src/core/Akka.Remote.TestKit/Player.cs#L510",
"region": {
"startLine": 510,
"startColumn": 113,
"startColumn": 110,
"endLine": 510,
"endColumn": 118
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/akka.net/src/core/Akka.TestKit/EventFilter/Internal/EventFilterApplier.cs#L410",
"region": {
"startLine": 410,
"startColumn": 42,
"startColumn": 39,
"endLine": 410,
"endColumn": 47
}
Expand All @@ -20,7 +20,7 @@
"uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/akka.net/src/core/Akka.TestKit/EventFilter/Internal/EventFilterApplier.cs#L433",
"region": {
"startLine": 433,
"startColumn": 42,
"startColumn": 39,
"endLine": 433,
"endColumn": 47
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,30 @@ private static string GetUnknownType(SyntaxKind kind) =>

#endif

public static bool IsTrue(this SyntaxNode node) =>
node switch
{
{ RawKind: (int)SyntaxKind.TrueLiteralExpression } => true, // true
{ RawKind: (int)SyntaxKind.LogicalNotExpression } => IsFalse(((PrefixUnaryExpressionSyntax)node).Operand), // !false
{ RawKind: (int)SyntaxKindEx.ConstantPattern } => IsTrue(((ConstantPatternSyntaxWrapper)node).Expression), // is true
{ RawKind: (int)SyntaxKindEx.NotPattern } => IsFalse(((UnaryPatternSyntaxWrapper)node).Pattern), // is not false
{ RawKind: (int)SyntaxKind.ParenthesizedExpression } => IsTrue(((ParenthesizedExpressionSyntax)node).Expression), // (true)
{ RawKind: (int)SyntaxKindEx.ParenthesizedPattern } => IsTrue(((ParenthesizedPatternSyntaxWrapper)node).Pattern), // is (true)
_ => false,
};

public static bool IsFalse(this SyntaxNode node) =>
node switch
{
{ RawKind: (int)SyntaxKind.FalseLiteralExpression } => true, // false
{ RawKind: (int)SyntaxKind.LogicalNotExpression } => IsTrue(((PrefixUnaryExpressionSyntax)node).Operand), // !true
{ RawKind: (int)SyntaxKindEx.ConstantPattern } => IsFalse(((ConstantPatternSyntaxWrapper)node).Expression), // is false
{ RawKind: (int)SyntaxKindEx.NotPattern } => IsTrue(((UnaryPatternSyntaxWrapper)node).Pattern), // is not true
{ RawKind: (int)SyntaxKind.ParenthesizedExpression } => IsFalse(((ParenthesizedExpressionSyntax)node).Expression), // (false)
{ RawKind: (int)SyntaxKindEx.ParenthesizedPattern } => IsFalse(((ParenthesizedPatternSyntaxWrapper)node).Pattern), // is (false)
_ => false,
};

private readonly record struct PathPosition(int Index, int TupleLength);

private sealed class ControlFlowGraphCache : ControlFlowGraphCacheBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,48 @@
namespace SonarAnalyzer.Rules.CSharp
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class BooleanLiteralUnnecessary : BooleanLiteralUnnecessaryBase<BinaryExpressionSyntax, SyntaxKind>
public sealed class BooleanLiteralUnnecessary : BooleanLiteralUnnecessaryBase<SyntaxKind>
{
protected override ILanguageFacade<SyntaxKind> Language => CSharpFacade.Instance;

protected override bool IsBooleanLiteral(SyntaxNode node) => IsTrueLiteralKind(node) || IsFalseLiteralKind(node);

protected override SyntaxNode GetLeftNode(BinaryExpressionSyntax binaryExpression) => binaryExpression.Left;

protected override SyntaxNode GetRightNode(BinaryExpressionSyntax binaryExpression) => binaryExpression.Right;

protected override SyntaxToken GetOperatorToken(BinaryExpressionSyntax binaryExpression) => binaryExpression.OperatorToken;
protected override SyntaxToken? GetOperatorToken(SyntaxNode node) =>
node switch
{
BinaryExpressionSyntax binary => binary.OperatorToken,
_ when IsPatternExpressionSyntaxWrapper.IsInstance(node) => ((IsPatternExpressionSyntaxWrapper)node).IsKeyword,
_ => null,
};

protected override bool IsTrueLiteralKind(SyntaxNode syntaxNode) => syntaxNode.IsKind(SyntaxKind.TrueLiteralExpression);
protected override bool IsTrue(SyntaxNode syntaxNode) => syntaxNode.IsTrue();

protected override bool IsFalseLiteralKind(SyntaxNode syntaxNode) => syntaxNode.IsKind(SyntaxKind.FalseLiteralExpression);
protected override bool IsFalse(SyntaxNode syntaxNode) => syntaxNode.IsFalse();

protected override bool IsInsideTernaryWithThrowExpression(BinaryExpressionSyntax syntaxNode) =>
protected override bool IsInsideTernaryWithThrowExpression(SyntaxNode syntaxNode) =>
syntaxNode.Parent is ConditionalExpressionSyntax conditionalExpression
&& (IsThrowExpression(conditionalExpression.WhenTrue) || IsThrowExpression(conditionalExpression.WhenFalse));

protected override SyntaxNode GetLeftNode(SyntaxNode node) =>
node switch
{
BinaryExpressionSyntax binaryExpression => binaryExpression.Left,
_ when IsPatternExpressionSyntaxWrapper.IsInstance(node) => ((IsPatternExpressionSyntaxWrapper)node).Expression,
_ => null
};

protected override SyntaxNode GetRightNode(SyntaxNode node) =>
node switch
{
BinaryExpressionSyntax binaryExpression => binaryExpression.Right,
_ when IsPatternExpressionSyntaxWrapper.IsInstance(node) => ((IsPatternExpressionSyntaxWrapper)node).Pattern,
_ => null
};

protected override void Initialize(SonarAnalysisContext context)
{
context.RegisterNodeAction(CheckLogicalNot, SyntaxKind.LogicalNotExpression);
context.RegisterNodeAction(CheckAndExpression, SyntaxKind.LogicalAndExpression);
context.RegisterNodeAction(CheckOrExpression, SyntaxKind.LogicalOrExpression);
context.RegisterNodeAction(CheckEquals, SyntaxKind.EqualsExpression);
context.RegisterNodeAction(CheckEquals, SyntaxKind.EqualsExpression, SyntaxKindEx.IsPatternExpression);
context.RegisterNodeAction(CheckNotEquals, SyntaxKind.NotEqualsExpression);
context.RegisterNodeAction(CheckConditional, SyntaxKind.ConditionalExpression);
context.RegisterNodeAction(CheckForLoopCondition, SyntaxKind.ForStatement);
Expand All @@ -67,13 +83,10 @@ private void CheckLogicalNot(SonarSyntaxNodeReportingContext context)
{
var logicalNot = (PrefixUnaryExpressionSyntax)context.Node;
var logicalNotOperand = logicalNot.Operand.RemoveParentheses();
if (IsBooleanLiteral(logicalNotOperand))
if (IsTrue(logicalNotOperand) || IsFalse(logicalNotOperand))
{
context.ReportIssue(Diagnostic.Create(Rule, logicalNot.Operand.GetLocation()));
}

static bool IsBooleanLiteral(SyntaxNode node) =>
node.IsKind(SyntaxKind.TrueLiteralExpression) || node.IsKind(SyntaxKind.FalseLiteralExpression);
}

private void CheckConditional(SonarSyntaxNodeReportingContext context)
Expand All @@ -94,7 +107,7 @@ private void CheckConditional(SonarSyntaxNodeReportingContext context)
{
return;
}
CheckTernaryExpressionBranches(context, conditional.SyntaxTree, whenTrue, whenFalse);
CheckTernaryExpressionBranches(context, whenTrue, whenFalse);
}

private static bool IsThrowExpression(ExpressionSyntax expressionSyntax) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Simplification;

namespace SonarAnalyzer.Rules.CSharp
{
Expand Down Expand Up @@ -54,6 +55,11 @@ protected override Task RegisterCodeFixesAsync(SyntaxNode root, SonarCodeFixCont
return Task.CompletedTask;
}

if (IsPatternExpressionSyntaxWrapper.IsInstance(syntaxNode))
{
RegisterPatternExpressionReplacement(context, root, (IsPatternExpressionSyntaxWrapper)syntaxNode);
}

if (syntaxNode is not LiteralExpressionSyntax literal)
{
return Task.CompletedTask;
Expand Down Expand Up @@ -86,6 +92,31 @@ protected override Task RegisterCodeFixesAsync(SyntaxNode root, SonarCodeFixCont
return Task.CompletedTask;
}

private static void RegisterPatternExpressionReplacement(SonarCodeFixContext context, SyntaxNode root, IsPatternExpressionSyntaxWrapper patternExpression)
{
var replacement = patternExpression.Pattern.SyntaxNode.IsTrue()
? patternExpression.Expression
: GetNegatedExpression(patternExpression.Expression);

if (replacement.IsTrue())
{
replacement = CSharpSyntaxHelper.TrueLiteralExpression;
}
else if (replacement.IsFalse())
{
replacement = CSharpSyntaxHelper.FalseLiteralExpression;
}

context.RegisterCodeFix(
Title,
c =>
{
var newRoot = root.ReplaceNode(patternExpression.SyntaxNode, replacement.WithAdditionalAnnotations(Formatter.Annotation));
return Task.FromResult(context.Document.WithSyntaxRoot(newRoot));
},
context.Diagnostics);
}

private static void RegisterForStatementConditionRemoval(SonarCodeFixContext context, SyntaxNode root, ForStatementSyntax forStatement) =>
context.RegisterCodeFix(
Title,
Expand All @@ -109,7 +140,7 @@ private static void RegisterBinaryExpressionRemoval(SonarCodeFixContext context,
Title,
c =>
{
var newExpression = GetNegatedExpression(otherNode);
var newExpression = GetNegatedExpression(otherNode).WithAdditionalAnnotations(Simplifier.Annotation);
var newRoot = root.ReplaceNode(binaryParent, newExpression
.WithAdditionalAnnotations(Formatter.Annotation));
Expand Down Expand Up @@ -141,7 +172,7 @@ private static void RegisterBinaryExpressionReplacement(SonarCodeFixContext cont
Title,
c =>
{
var keepThisNode = FindNodeToKeep(binary);
var keepThisNode = FindNodeToKeep(binary).WithAdditionalAnnotations(Simplifier.Annotation);
var newRoot = root.ReplaceNode(syntaxNode, keepThisNode
.WithAdditionalAnnotations(Formatter.Annotation));
return Task.FromResult(context.Document.WithSyntaxRoot(newRoot));
Expand Down
Loading

0 comments on commit 8f6674f

Please sign in to comment.