From f1715bb3a35df2470154b6e5542469bd71af698a Mon Sep 17 00:00:00 2001 From: joeriddles Date: Mon, 8 Jan 2024 12:07:54 -0800 Subject: [PATCH 1/8] Add failing test --- .../DateTime_ImplicitConversion_InLinq.cs | 27 +++++ .../DateTimeConversionTests.cs | 105 ++++++++++++++++-- .../IntelliTect.Analyzer.Tests.csproj | 4 + 3 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Data/DateTime_ImplicitConversion_InLinq.cs diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Data/DateTime_ImplicitConversion_InLinq.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Data/DateTime_ImplicitConversion_InLinq.cs new file mode 100644 index 00000000..933c9008 --- /dev/null +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Data/DateTime_ImplicitConversion_InLinq.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ConsoleApp1 +{ + internal class Pair + { + public DateTimeOffset DateTimeOffset { get; init; } + public DateTime DateTime { get; init; } + + public Pair(DateTimeOffset dateTimeOffset, DateTime dateTime) + { + DateTimeOffset = dateTimeOffset; + DateTime = dateTime; + } + } + + internal class Program + { + static void Main(string[] args) + { + List list = new(){ new(DateTimeOffset.Now, DateTime.Now) }; + _ = list.Where(pair => pair.DateTimeOffset < pair.DateTime); + } + } +} diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs index 12ef0640..9fdec794 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs @@ -1,4 +1,6 @@ -using Microsoft.CodeAnalysis; +using System; +using System.IO; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.VisualStudio.TestTools.UnitTesting; using TestHelper; @@ -40,7 +42,7 @@ static void Main(string[] args) } [TestMethod] - public void UsageOfImplicitConversionInComparison_ProducesWarningMessage() + public void UsageOfImplicitConversionInComparison_DateTimeToDateTimeOffset_ProducesWarningMessage() { string source = @" using System; @@ -53,15 +55,42 @@ internal class Program static void Main(string[] args) { DateTime first = DateTime.Now; + DateTimeOffset second = DateTimeOffset.Now; + _ = first < second + } + } +}"; - Thread.Sleep(10); + VerifyCSharpDiagnostic(source, + new DiagnosticResult + { + Id = "INTL0202", + Severity = DiagnosticSeverity.Warning, + Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", + Locations = + new[] { + new DiagnosticResultLocation("Test0.cs", 13, 17) + } + }); - DateTimeOffset second = DateTimeOffset.Now; + } - if (first < second) - { - Console.WriteLine(""Time has passed...""); - } + [TestMethod] + public void UsageOfImplicitConversionInComparison_DateTimeOffsetToDateTime_ProducesWarningMessage() + { + string source = @" +using System; +using System.Threading; + +namespace ConsoleApp1 +{ + internal class Program + { + static void Main(string[] args) + { + DateTimeOffset first = DateTimeOffset.Now; + DateTime second = DateTime.Now; + _ = first < second } } }"; @@ -74,12 +103,70 @@ static void Main(string[] args) Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", Locations = new[] { - new DiagnosticResultLocation("Test0.cs", 17, 17) + new DiagnosticResultLocation("Test0.cs", 13, 25) } }); } + [TestMethod] + public void UsageOfImplicitConversionInComparison_NullableDateTimeOffsetToDateTime_ProducesWarningMessage() + { + string source = @" +using System; +using System.Threading; + +namespace ConsoleApp1 +{ + internal class Program + { + static void Main(string[] args) + { + DateTimeOffset? first = DateTimeOffset.Now; + DateTime second = DateTime.Now; + _ = first < second + } + } +}"; + + VerifyCSharpDiagnostic( + source, + new DiagnosticResult + { + Id = "INTL0202", + Severity = DiagnosticSeverity.Warning, + Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", + Locations = new[] { + new DiagnosticResultLocation("Test0.cs", 13, 25) + } + } + ); + + } + + [TestMethod] + public void UsageOfImplicitConversion_InLinq_ProducesWarningMessage() + { + string dir = Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName; + string path = Path.Combine(dir, "Data", "DateTime_ImplicitConversion_InLinq.cs"); + string source = File.ReadAllText(path); + + VerifyCSharpDiagnostic( + source, + new DiagnosticResult + { + Id = "INTL0202", + Severity = DiagnosticSeverity.Warning, + Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", + Locations = + new[] { + new DiagnosticResultLocation("Test0.cs", 17, 25) + } + } + ); + } + + [TestMethod] public void UsageOfExplicitConversion_ProducesNothing() { diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/IntelliTect.Analyzer.Tests.csproj b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/IntelliTect.Analyzer.Tests.csproj index 063e1762..75c18afd 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/IntelliTect.Analyzer.Tests.csproj +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/IntelliTect.Analyzer.Tests.csproj @@ -5,6 +5,10 @@ CA2007,CA1815,CA1303,CA1707,CA1305 + + + + all From c382bcb374e5749c17340a6e69f26e28c4bc2324 Mon Sep 17 00:00:00 2001 From: joeriddles Date: Tue, 9 Jan 2024 09:26:50 -0800 Subject: [PATCH 2/8] Fix failing test line --- .../IntelliTect.Analyzer.Test/DateTimeConversionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs index 730b6f3a..4523d52a 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs @@ -64,7 +64,7 @@ static void Main(string[] args) Id = "INTL0202", Severity = DiagnosticSeverity.Warning, Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", - Locations = [new DiagnosticResultLocation("Test0.cs", 17, 17)] + Locations = [new DiagnosticResultLocation("Test0.cs", 13, 17)] }); } From 96f874d41ddc70fd79cb4aa5bc82fc6c1e4ebd35 Mon Sep 17 00:00:00 2001 From: joeriddles Date: Tue, 9 Jan 2024 09:44:27 -0800 Subject: [PATCH 3/8] Add testing to workflow --- .github/workflows/dotnetBuild.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnetBuild.yml b/.github/workflows/dotnetBuild.yml index 12721078..63c51149 100644 --- a/.github/workflows/dotnetBuild.yml +++ b/.github/workflows/dotnetBuild.yml @@ -40,4 +40,7 @@ jobs: run: dotnet restore - name: Build - run: dotnet build -p:ContinuousIntegrationBuild=True --no-restore --configuration Release \ No newline at end of file + run: dotnet build -p:ContinuousIntegrationBuild=True --no-restore --configuration Release + + - name: Test + run: dotnet test IntelliTect.Analyzer.sln /property:GenerateFullPaths=true /consoleloggerparameters:NoSummary From 9f3ce7047797e956300f9da54a541821ddd513b2 Mon Sep 17 00:00:00 2001 From: joeriddles Date: Tue, 9 Jan 2024 09:45:45 -0800 Subject: [PATCH 4/8] Revert dotnet test in workflow to before it was broken --- .github/workflows/dotnetBuild.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnetBuild.yml b/.github/workflows/dotnetBuild.yml index 63c51149..5a4e8107 100644 --- a/.github/workflows/dotnetBuild.yml +++ b/.github/workflows/dotnetBuild.yml @@ -43,4 +43,4 @@ jobs: run: dotnet build -p:ContinuousIntegrationBuild=True --no-restore --configuration Release - name: Test - run: dotnet test IntelliTect.Analyzer.sln /property:GenerateFullPaths=true /consoleloggerparameters:NoSummary + run: dotnet test --no-build --verbosity normal From 25c0a0c39ee36b0d988a2973b3da92652a544bbc Mon Sep 17 00:00:00 2001 From: joeriddles Date: Tue, 9 Jan 2024 10:35:43 -0800 Subject: [PATCH 5/8] Add UsageOfImplicitConversion_InLinqWithVariables_ProducesWarningMessage --- .../DateTime_ImplicitConversion_InLinq.cs | 27 -------- .../DateTimeConversionTests.cs | 61 +++++++++++++++++-- .../IntelliTect.Analyzer.Tests.csproj | 4 -- 3 files changed, 57 insertions(+), 35 deletions(-) delete mode 100644 IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Data/DateTime_ImplicitConversion_InLinq.cs diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Data/DateTime_ImplicitConversion_InLinq.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Data/DateTime_ImplicitConversion_InLinq.cs deleted file mode 100644 index 933c9008..00000000 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Data/DateTime_ImplicitConversion_InLinq.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ConsoleApp1 -{ - internal class Pair - { - public DateTimeOffset DateTimeOffset { get; init; } - public DateTime DateTime { get; init; } - - public Pair(DateTimeOffset dateTimeOffset, DateTime dateTime) - { - DateTimeOffset = dateTimeOffset; - DateTime = dateTime; - } - } - - internal class Program - { - static void Main(string[] args) - { - List list = new(){ new(DateTimeOffset.Now, DateTime.Now) }; - _ = list.Where(pair => pair.DateTimeOffset < pair.DateTime); - } - } -} diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs index 4523d52a..4d9d785f 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs @@ -133,12 +133,65 @@ static void Main(string[] args) } + [TestMethod] + public void UsageOfImplicitConversion_InLinqWithVariables_ProducesWarningMessage() + { + string source = @" +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ConsoleApp1 +{ + internal class Pair(DateTimeOffset DateTimeOffset, DateTime DateTime); + + internal class Program + { + static void Main(string[] args) + { + List list = new(){ new(DateTimeOffset.Now, DateTime.Now) }; + _ = list.Where(pair => { + DateTimeOffset first = pair.DateTimeOffset; + DateTime second = pair.DateTime; + return first < second; + }); + } + } +}"; + + VerifyCSharpDiagnostic( + source, + new DiagnosticResult + { + Id = "INTL0202", + Severity = DiagnosticSeverity.Warning, + Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", + Locations = [new DiagnosticResultLocation("Test0.cs", 18, 32)] + } + ); + } + [TestMethod] public void UsageOfImplicitConversion_InLinq_ProducesWarningMessage() { - string dir = Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName; - string path = Path.Combine(dir, "Data", "DateTime_ImplicitConversion_InLinq.cs"); - string source = File.ReadAllText(path); + string source = @" +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ConsoleApp1 +{ + internal class Pair(DateTimeOffset DateTimeOffset, DateTime DateTime); + + internal class Program + { + static void Main(string[] args) + { + List list = new(){ new(DateTimeOffset.Now, DateTime.Now) }; + _ = list.Where(pair => pair.DateTimeOffset < pair.DateTime); + } + } +}"; VerifyCSharpDiagnostic( source, @@ -147,7 +200,7 @@ public void UsageOfImplicitConversion_InLinq_ProducesWarningMessage() Id = "INTL0202", Severity = DiagnosticSeverity.Warning, Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", - Locations = [new DiagnosticResultLocation("Test0.cs", 17, 25)] + Locations = [new DiagnosticResultLocation("Test0.cs", 15, 36)] } ); } diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/IntelliTect.Analyzer.Tests.csproj b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/IntelliTect.Analyzer.Tests.csproj index 29364a45..0a3961f6 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/IntelliTect.Analyzer.Tests.csproj +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/IntelliTect.Analyzer.Tests.csproj @@ -5,10 +5,6 @@ CA2007,CA1815,CA1303,CA1707,CA1305 - - - - all From 0f9b281992909518a68a7dc443ff1e84e9143da3 Mon Sep 17 00:00:00 2001 From: joeriddles Date: Tue, 9 Jan 2024 12:12:24 -0800 Subject: [PATCH 6/8] Fix failing test for property --- .../DateTimeConversionTests.cs | 2 +- ...licitDateTimeToDateTimeOffsetConversion.cs | 24 +++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs index 4d9d785f..dfd6a315 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs @@ -200,7 +200,7 @@ static void Main(string[] args) Id = "INTL0202", Severity = DiagnosticSeverity.Warning, Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", - Locations = [new DiagnosticResultLocation("Test0.cs", 15, 36)] + Locations = [new DiagnosticResultLocation("Test0.cs", 14, 36)] } ); } diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer/Analyzers/BanImplicitDateTimeToDateTimeOffsetConversion.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer/Analyzers/BanImplicitDateTimeToDateTimeOffsetConversion.cs index 46940659..af738d22 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer/Analyzers/BanImplicitDateTimeToDateTimeOffsetConversion.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer/Analyzers/BanImplicitDateTimeToDateTimeOffsetConversion.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Immutable; +using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; @@ -33,22 +34,35 @@ public override void Initialize(AnalysisContext context) private void AnalyzeInvocation(OperationAnalysisContext context) { - if (context.Operation is not IConversionOperation conversionOperation) + if (context.Operation is not IConversionOperation conversionOperation || !conversionOperation.Conversion.IsImplicit) { return; } - if (conversionOperation.Conversion.IsImplicit && conversionOperation.Conversion.MethodSymbol is object && conversionOperation.Conversion.MethodSymbol.ContainingType is object) + if (conversionOperation.Conversion.MethodSymbol is object && conversionOperation.Conversion.MethodSymbol.ContainingType is object) { INamedTypeSymbol containingType = conversionOperation.Conversion.MethodSymbol.ContainingType; - INamedTypeSymbol dateTimeOffsetType = context.Compilation.GetTypeByMetadataName("System.DateTimeOffset"); - if (SymbolEqualityComparer.Default.Equals(containingType, dateTimeOffsetType)) + if (IsDateTimeOffsetSymbol(context, containingType)) { context.ReportDiagnostic(Diagnostic.Create(_Rule202, conversionOperation.Syntax.GetLocation())); } } + else + { + bool hasImplicitArg = conversionOperation.Operand.ChildOperations + .Where(op => op.Kind == OperationKind.Argument && IsDateTimeOffsetSymbol(context, ((IArgumentOperation)op).Value.Type)) + .Any(); + if (hasImplicitArg) + { + context.ReportDiagnostic(Diagnostic.Create(_Rule202, conversionOperation.Syntax.GetLocation())); + } + } + } - + private static bool IsDateTimeOffsetSymbol(OperationAnalysisContext context, ITypeSymbol symbol) + { + INamedTypeSymbol dateTimeOffsetType = context.Compilation.GetTypeByMetadataName("System.DateTimeOffset"); + return SymbolEqualityComparer.Default.Equals(symbol, dateTimeOffsetType); } private static class Rule202 From c0bb06edb50614d9c23a2ef3644399c4b36a0e42 Mon Sep 17 00:00:00 2001 From: joeriddles Date: Tue, 9 Jan 2024 12:54:49 -0800 Subject: [PATCH 7/8] Fix ordering of actual and expected --- .../Verifiers/DiagnosticVerifier.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Verifiers/DiagnosticVerifier.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Verifiers/DiagnosticVerifier.cs index 134d643f..887f1a0f 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Verifiers/DiagnosticVerifier.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/Verifiers/DiagnosticVerifier.cs @@ -167,16 +167,16 @@ private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagno // Only check line position if there is an actual line in the real diagnostic if (actualLinePosition.Line > 0) { - Assert.AreEqual(actualLinePosition.Line + 1, - expected.Line, + Assert.AreEqual(expected.Line, + actualLinePosition.Line + 1, $"Expected diagnostic to be on line \"{expected.Line}\" was actually on line \"{actualLinePosition.Line + 1}\"{Environment.NewLine}{Environment.NewLine}Diagnostic:{Environment.NewLine} {FormatDiagnostics(analyzer, diagnostic)}{Environment.NewLine}"); } // Only check column position if there is an actual column position in the real diagnostic if (actualLinePosition.Character > 0) { - Assert.AreEqual(actualLinePosition.Character + 1, - expected.Column, + Assert.AreEqual(expected.Column, + actualLinePosition.Character + 1, $"Expected diagnostic to start at column \"{expected.Column}\" was actually at column \"{actualLinePosition.Character + 1}\"{Environment.NewLine}{Environment.NewLine}Diagnostic:{Environment.NewLine} {FormatDiagnostics(analyzer, diagnostic)}{Environment.NewLine}"); } } From 6779ec79f35ed7653814e9cb11fcd4e267351350 Mon Sep 17 00:00:00 2001 From: joeriddles Date: Tue, 9 Jan 2024 12:54:57 -0800 Subject: [PATCH 8/8] WIP refactor --- .../DateTimeConversionTests.cs | 57 +++++++++++++++---- ...licitDateTimeToDateTimeOffsetConversion.cs | 8 +-- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs index dfd6a315..12878b32 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/DateTimeConversionTests.cs @@ -134,7 +134,7 @@ static void Main(string[] args) } [TestMethod] - public void UsageOfImplicitConversion_InLinqWithVariables_ProducesWarningMessage() + public void UsageOfImplicitConversion_WithProperties_ProducesWarningMessage() { string source = @" using System; @@ -149,12 +149,8 @@ internal class Program { static void Main(string[] args) { - List list = new(){ new(DateTimeOffset.Now, DateTime.Now) }; - _ = list.Where(pair => { - DateTimeOffset first = pair.DateTimeOffset; - DateTime second = pair.DateTime; - return first < second; - }); + Pair pair = new(DateTimeOffset.Now, DateTime.Now); + retirn pair.DateTimeOffset < pair.DateTime; } } }"; @@ -166,14 +162,14 @@ static void Main(string[] args) Id = "INTL0202", Severity = DiagnosticSeverity.Warning, Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", - Locations = [new DiagnosticResultLocation("Test0.cs", 18, 32)] + Locations = [new DiagnosticResultLocation("Test0.cs", 14, 29)] } ); } [TestMethod] - public void UsageOfImplicitConversion_InLinq_ProducesWarningMessage() - { + public void UsageOfImplicitConversion_WithPropertiesInLinq_ProducesWarningMessage() + { string source = @" using System; using System.Collections.Generic; @@ -187,7 +183,7 @@ internal class Program { static void Main(string[] args) { - List list = new(){ new(DateTimeOffset.Now, DateTime.Now) }; + List list = new(){ new(DateTimeOffset.Now, DateTime.Now) }; // <-- L14 _ = list.Where(pair => pair.DateTimeOffset < pair.DateTime); } } @@ -200,11 +196,48 @@ static void Main(string[] args) Id = "INTL0202", Severity = DiagnosticSeverity.Warning, Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", - Locations = [new DiagnosticResultLocation("Test0.cs", 14, 36)] + Locations = [new DiagnosticResultLocation("Test0.cs", 14, 42)] } ); } + [TestMethod] + public void UsageOfImplicitConversion_InLinqWithVariables_ProducesWarningMessage() + { + string source = @" +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ConsoleApp1 +{ + internal class Pair(DateTimeOffset DateTimeOffset, DateTime DateTime); + + internal class Program + { + static void Main(string[] args) + { + List list = new(){ new(DateTimeOffset.Now, DateTime.Now) }; + _ = list.Where(pair => { + DateTimeOffset first = pair.DateTimeOffset; + DateTime second = pair.DateTime; + return first < second; + }); + } + } +}"; + + VerifyCSharpDiagnostic( + source, + new DiagnosticResult + { + Id = "INTL0202", + Severity = DiagnosticSeverity.Warning, + Message = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior", + Locations = [new DiagnosticResultLocation("Test0.cs", 18, 24)] + } + ); + } [TestMethod] public void UsageOfExplicitConversion_ProducesNothing() diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer/Analyzers/BanImplicitDateTimeToDateTimeOffsetConversion.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer/Analyzers/BanImplicitDateTimeToDateTimeOffsetConversion.cs index af738d22..3a148486 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer/Analyzers/BanImplicitDateTimeToDateTimeOffsetConversion.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer/Analyzers/BanImplicitDateTimeToDateTimeOffsetConversion.cs @@ -49,12 +49,12 @@ private void AnalyzeInvocation(OperationAnalysisContext context) } else { - bool hasImplicitArg = conversionOperation.Operand.ChildOperations + IOperation implicitDateTimeOffsetOp = conversionOperation.Operand.ChildOperations .Where(op => op.Kind == OperationKind.Argument && IsDateTimeOffsetSymbol(context, ((IArgumentOperation)op).Value.Type)) - .Any(); - if (hasImplicitArg) + .FirstOrDefault(); + if (implicitDateTimeOffsetOp != default) { - context.ReportDiagnostic(Diagnostic.Create(_Rule202, conversionOperation.Syntax.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(_Rule202, implicitDateTimeOffsetOp.Syntax.GetLocation())); } } }