Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,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",
Message = "Using 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' or 'new DateTimeOffset(DateTime)' can result in unpredictable behavior",
Locations =
[
new DiagnosticResultLocation("Test0.cs", 10, 38)
Expand Down Expand Up @@ -71,7 +71,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",
Message = "Using 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' or 'new DateTimeOffset(DateTime)' can result in unpredictable behavior",
Locations =
[
new DiagnosticResultLocation("Test0.cs", 17, 17)
Expand Down Expand Up @@ -101,6 +101,110 @@ static void Main(string[] args)

}

[TestMethod]
public void UsageOfDateTimeOffsetConstructorWithDateTime_ProducesWarningMessage()
{
string source = @"
using System;

namespace ConsoleApp44
{
class Program
{
static void Main(string[] args)
{
DateTimeOffset ofs = new DateTimeOffset(DateTime.Now);
}
}
}";

VerifyCSharpDiagnostic(source,
new DiagnosticResult
{
Id = "INTL0202",
Severity = DiagnosticSeverity.Warning,
Message = "Using 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' or 'new DateTimeOffset(DateTime)' can result in unpredictable behavior",
Locations =
[
new DiagnosticResultLocation("Test0.cs", 10, 38)
]
});

}

[TestMethod]
public void UsageOfDateTimeOffsetConstructorWithDateTimeAndTimeSpan_ProducesNothing()
{
string source = @"
using System;

namespace ConsoleApp45
{
class Program
{
static void Main(string[] args)
{
DateTimeOffset ofs = new DateTimeOffset(DateTime.Now, TimeSpan.Zero);
}
}
}";

VerifyCSharpDiagnostic(source);

}

[TestMethod]
public void UsageOfDateTimeOffsetConstructorWithYearMonthDay_ProducesNothing()
{
string source = @"
using System;

namespace ConsoleApp46
{
class Program
{
static void Main(string[] args)
{
DateTimeOffset ofs = new DateTimeOffset(2022, 2, 2, 0, 0, 0, TimeSpan.Zero);
}
}
}";

VerifyCSharpDiagnostic(source);

}

[TestMethod]
public void UsageOfDateTimeOffsetConstructorWithNewDateTime_ProducesWarningMessage()
{
string source = @"
using System;

namespace ConsoleApp47
{
class Program
{
static void Main(string[] args)
{
DateTimeOffset dto = new DateTimeOffset(new DateTime(2022, 2, 2));
}
}
}";

VerifyCSharpDiagnostic(source,
new DiagnosticResult
{
Id = "INTL0202",
Severity = DiagnosticSeverity.Warning,
Message = "Using 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' or 'new DateTimeOffset(DateTime)' can result in unpredictable behavior",
Locations =
[
new DiagnosticResultLocation("Test0.cs", 10, 38)
]
});

}

protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
{
return new Analyzers.BanImplicitDateTimeToDateTimeOffsetConversion();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public override void Initialize(AnalysisContext context)
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterOperationAction(AnalyzeInvocation, OperationKind.Conversion);
context.RegisterOperationAction(AnalyzeObjectCreation, OperationKind.ObjectCreation);
}

private void AnalyzeInvocation(OperationAnalysisContext context)
Expand All @@ -51,11 +52,40 @@ private void AnalyzeInvocation(OperationAnalysisContext context)

}

private void AnalyzeObjectCreation(OperationAnalysisContext context)
{
if (context.Operation is not IObjectCreationOperation objectCreation)
{
return;
}

INamedTypeSymbol dateTimeOffsetType = context.Compilation.GetTypeByMetadataName("System.DateTimeOffset")
?? throw new InvalidOperationException("Unable to find DateTimeOffset type");
INamedTypeSymbol dateTimeType = context.Compilation.GetTypeByMetadataName("System.DateTime")
?? throw new InvalidOperationException("Unable to find DateTime type");

// Check if we're creating a DateTimeOffset
if (!SymbolEqualityComparer.Default.Equals(objectCreation.Type, dateTimeOffsetType))
{
return;
}

// Check if the constructor has exactly one parameter and it's a DateTime
if (objectCreation.Constructor?.Parameters.Length == 1)
{
IParameterSymbol parameter = objectCreation.Constructor.Parameters[0];
if (SymbolEqualityComparer.Default.Equals(parameter.Type, dateTimeType))
{
context.ReportDiagnostic(Diagnostic.Create(_Rule202, objectCreation.Syntax.GetLocation()));
}
}
}

private static class Rule202
{
internal const string DiagnosticId = "INTL0202";
internal const string Title = "Do not use implicit conversion from `DateTime` to `DateTimeOffset`";
internal const string MessageFormat = "Using the symbol 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' can result in unpredictable behavior";
internal const string MessageFormat = "Using 'DateTimeOffset.implicit operator DateTimeOffset(DateTime)' or 'new DateTimeOffset(DateTime)' can result in unpredictable behavior";
#pragma warning disable INTL0001 // Allow field to not be prefixed with an underscore to match the style
internal static readonly string HelpMessageUri = DiagnosticUrlBuilder.GetUrl(Title,
DiagnosticId);
Expand Down
Loading