diff --git a/README.md b/README.md
index 05e89132..926b0169 100755
--- a/README.md
+++ b/README.md
@@ -187,6 +187,7 @@ If you are already using other analyzers, you can check [which rules are duplica
|[MA0169](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0169.md)|Design|Use Equals method instead of operator|⚠️|✔️|❌|
|[MA0170](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0170.md)|Design|Type cannot be used as an attribute argument|⚠️|❌|❌|
|[MA0171](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0171.md)|Usage|Use pattern matching instead of inequality operators for discrete value|ℹ️|❌|✔️|
+|[MA0172](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0172.md)|Usage|Both sides of the logical operation are identical|⚠️|❌|❌|
diff --git a/docs/README.md b/docs/README.md
index afc32fdc..2edf082c 100755
--- a/docs/README.md
+++ b/docs/README.md
@@ -171,6 +171,7 @@
|[MA0169](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0169.md)|Design|Use Equals method instead of operator|⚠️|✔️|❌|
|[MA0170](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0170.md)|Design|Type cannot be used as an attribute argument|⚠️|❌|❌|
|[MA0171](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0171.md)|Usage|Use pattern matching instead of inequality operators for discrete value|ℹ️|❌|✔️|
+|[MA0172](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0172.md)|Usage|Both sides of the logical operation are identical|⚠️|❌|❌|
|Id|Suppressed rule|Justification|
|--|---------------|-------------|
@@ -692,6 +693,9 @@ dotnet_diagnostic.MA0170.severity = none
# MA0171: Use pattern matching instead of inequality operators for discrete value
dotnet_diagnostic.MA0171.severity = none
+
+# MA0172: Both sides of the logical operation are identical
+dotnet_diagnostic.MA0172.severity = none
```
# .editorconfig - all rules disabled
@@ -1206,4 +1210,7 @@ dotnet_diagnostic.MA0170.severity = none
# MA0171: Use pattern matching instead of inequality operators for discrete value
dotnet_diagnostic.MA0171.severity = none
+
+# MA0172: Both sides of the logical operation are identical
+dotnet_diagnostic.MA0172.severity = none
```
diff --git a/docs/Rules/MA0172.md b/docs/Rules/MA0172.md
new file mode 100644
index 00000000..24c9bd0a
--- /dev/null
+++ b/docs/Rules/MA0172.md
@@ -0,0 +1,8 @@
+# MA0172 - Both sides of the logical operation are identical
+
+This rule triggers when both sides of a logical operation (such as `&&`, `||`, `&`, `|`, `==`, `!=`, or pattern matching operations) are identical. This usually indicates a mistake or redundant code.
+
+```csharp
+_ = x == x; // non-compliant
+_ = x is true or true; // non-compliant
+```
diff --git a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig
index ba221cee..bafccfa2 100644
--- a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig
+++ b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig
@@ -511,3 +511,6 @@ dotnet_diagnostic.MA0170.severity = none
# MA0171: Use pattern matching instead of inequality operators for discrete value
dotnet_diagnostic.MA0171.severity = none
+
+# MA0172: Both sides of the logical operation are identical
+dotnet_diagnostic.MA0172.severity = none
diff --git a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig
index ad31e62d..6dedb33a 100644
--- a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig
+++ b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig
@@ -511,3 +511,6 @@ dotnet_diagnostic.MA0170.severity = none
# MA0171: Use pattern matching instead of inequality operators for discrete value
dotnet_diagnostic.MA0171.severity = none
+
+# MA0172: Both sides of the logical operation are identical
+dotnet_diagnostic.MA0172.severity = none
diff --git a/src/Meziantou.Analyzer/RuleIdentifiers.cs b/src/Meziantou.Analyzer/RuleIdentifiers.cs
index a48b05a6..509e8a70 100755
--- a/src/Meziantou.Analyzer/RuleIdentifiers.cs
+++ b/src/Meziantou.Analyzer/RuleIdentifiers.cs
@@ -174,6 +174,7 @@ internal static class RuleIdentifiers
public const string UseEqualsMethodInsteadOfOperator = "MA0169";
public const string TypeCannotBeUsedInAnAttributeParameter = "MA0170";
public const string UsePatternMatchingInsteadOfHasvalue = "MA0171";
+ public const string BothSideOfTheConditionAreIdentical = "MA0172";
public static string GetHelpUri(string identifier)
{
diff --git a/src/Meziantou.Analyzer/Rules/BothSideOfTheConditionAreIdenticalAnalyzer.cs b/src/Meziantou.Analyzer/Rules/BothSideOfTheConditionAreIdenticalAnalyzer.cs
new file mode 100644
index 00000000..539b2f8e
--- /dev/null
+++ b/src/Meziantou.Analyzer/Rules/BothSideOfTheConditionAreIdenticalAnalyzer.cs
@@ -0,0 +1,56 @@
+using System.Collections.Immutable;
+using Meziantou.Analyzer.Internals;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
+
+namespace Meziantou.Analyzer.Rules;
+
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public class BothSideOfTheConditionAreIdenticalAnalyzer : DiagnosticAnalyzer
+{
+ private static readonly DiagnosticDescriptor Rule = new(
+ RuleIdentifiers.BothSideOfTheConditionAreIdentical,
+ title: "Both sides of the logical operation are identical",
+ messageFormat: "Both sides of the logical operation are identical",
+ RuleCategories.Usage,
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: false,
+ helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.BothSideOfTheConditionAreIdentical));
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.EnableConcurrentExecution();
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+
+
+ context.RegisterOperationAction(AnalyzeBinaryOperation, OperationKind.Binary);
+ context.RegisterOperationAction(AnalyzeBinaryPatternOperation, OperationKind.BinaryPattern);
+ }
+
+ private void AnalyzeBinaryOperation(OperationAnalysisContext context)
+ {
+ var operation = (IBinaryOperation)context.Operation;
+ if (operation.OperatorKind is BinaryOperatorKind.ConditionalAnd or BinaryOperatorKind.ConditionalOr or BinaryOperatorKind.And or BinaryOperatorKind.Or or BinaryOperatorKind.Equals or BinaryOperatorKind.NotEquals)
+ {
+ if (operation.Type.IsBoolean() && operation.LeftOperand.Syntax.IsEquivalentTo(operation.RightOperand.Syntax, topLevel: false))
+ {
+ context.ReportDiagnostic(Rule, operation);
+ }
+ }
+ }
+
+ private void AnalyzeBinaryPatternOperation(OperationAnalysisContext context)
+ {
+ var operation = (IBinaryPatternOperation)context.Operation;
+ if (operation.OperatorKind is BinaryOperatorKind.And or BinaryOperatorKind.Or)
+ {
+ if (operation.LeftPattern.Syntax.IsEquivalentTo(operation.RightPattern.Syntax, topLevel: false))
+ {
+ context.ReportDiagnostic(Rule, operation);
+ }
+ }
+ }
+}
diff --git a/tests/Meziantou.Analyzer.Test/Rules/BothSideOfTheConditionAreIdenticalAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/BothSideOfTheConditionAreIdenticalAnalyzerTests.cs
new file mode 100644
index 00000000..1e1f8c05
--- /dev/null
+++ b/tests/Meziantou.Analyzer.Test/Rules/BothSideOfTheConditionAreIdenticalAnalyzerTests.cs
@@ -0,0 +1,60 @@
+using Meziantou.Analyzer.Rules;
+using TestHelper;
+using Xunit;
+
+namespace Meziantou.Analyzer.Test.Rules;
+
+public sealed class BothSideOfTheConditionAreIdenticalAnalyzerTests
+{
+ private static ProjectBuilder CreateProjectBuilder()
+ {
+ return new ProjectBuilder()
+ .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.ConsoleApplication)
+ .WithAnalyzer();
+ }
+
+ [Theory]
+ [InlineData("a == b")]
+ [InlineData("a != b")]
+ [InlineData("a & b")]
+ [InlineData("a && b")]
+ [InlineData("a | b")]
+ [InlineData("a || b")]
+ [InlineData("a is false")]
+ [InlineData("a is true")]
+ [InlineData("a is false or true")]
+ [InlineData("a is false and not true")]
+ public async Task DifferentCode(string expression)
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode($$"""
+ var a = false;
+ var b = false;
+ var c = 0;
+ _ = {{expression}};
+ """)
+ .ValidateAsync();
+ }
+
+ [Theory]
+ [InlineData("[|a == a|]")]
+ [InlineData("[|a != a|]")]
+ [InlineData("[|a & a|]")]
+ [InlineData("[|a && a|]")]
+ [InlineData("[|a | a|]")]
+ [InlineData("[|a || a|]")]
+ [InlineData("a is [|true or true|]")]
+ [InlineData("a is [|true and true|]")]
+ public async Task SameCode(string expression)
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode($$"""
+ var a = false;
+ var b = false;
+ var c = 0;
+ _ = {{expression}};
+ """)
+ .ValidateAsync();
+ }
+
+}