Skip to content

Commit

Permalink
New rule: MA0133 - Use DateTimeOffset instead of DateTime to avoid an…
Browse files Browse the repository at this point in the history
… implicit conversion (#506)
  • Loading branch information
meziantou authored Apr 25, 2023
1 parent e679d84 commit df707da
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 7 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ If you are already using other analyzers, you can check [which rules are duplica
|[MA0130](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0130.md)|Usage|GetType() should not be used on System.Type instances|⚠️|✔️||
|[MA0131](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0131.md)|Usage|ArgumentNullException.ThrowIfNull should not be used with non-nullable types|⚠️|✔️||
|[MA0132](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0132.md)|Design|Do not convert implicitly to DateTimeOffset|⚠️|✔️||
|[MA0133](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0133.md)|Design|Use DateTimeOffset instead of relying on the implicit conversion|ℹ️|✔️||

<!-- rules -->

Expand Down
7 changes: 7 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
|[MA0130](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0130.md)|Usage|GetType() should not be used on System.Type instances|<span title='Warning'>⚠️</span>|✔️||
|[MA0131](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0131.md)|Usage|ArgumentNullException.ThrowIfNull should not be used with non-nullable types|<span title='Warning'>⚠️</span>|✔️||
|[MA0132](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0132.md)|Design|Do not convert implicitly to DateTimeOffset|<span title='Warning'>⚠️</span>|✔️||
|[MA0133](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0133.md)|Design|Use DateTimeOffset instead of relying on the implicit conversion|<span title='Info'>ℹ️</span>|✔️||

|Id|Suppressed rule|Justification|
|--|---------------|-------------|
Expand Down Expand Up @@ -534,6 +535,9 @@ dotnet_diagnostic.MA0131.severity = warning
# MA0132: Do not convert implicitly to DateTimeOffset
dotnet_diagnostic.MA0132.severity = warning
# MA0133: Use DateTimeOffset instead of relying on the implicit conversion
dotnet_diagnostic.MA0133.severity = suggestion
```

# .editorconfig - all rules disabled
Expand Down Expand Up @@ -931,4 +935,7 @@ dotnet_diagnostic.MA0131.severity = none
# MA0132: Do not convert implicitly to DateTimeOffset
dotnet_diagnostic.MA0132.severity = none
# MA0133: Use DateTimeOffset instead of relying on the implicit conversion
dotnet_diagnostic.MA0133.severity = none
```
11 changes: 11 additions & 0 deletions docs/Rules/MA0133.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# MA0133 - Use DateTimeOffset instead of relying on the implicit conversion

Replace `DateTime.UtcNow` or `DateTime.Now` with `DateTimeOffset.UtcNow` or `DateTimeOffset.Now` to avoid an implicit conversion.

````c#
Sample(DateTime.UtcNow); // non-compliant
Sample(DateTimeOffset.UtcNow); // ok
void Sample(DateTimeOffset datetime) { }
````
1 change: 1 addition & 0 deletions src/Meziantou.Analyzer/RuleIdentifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ internal static class RuleIdentifiers
public const string ObjectGetTypeOnTypeInstance = "MA0130";
public const string ThrowIfNullWithNonNullableInstance = "MA0131";
public const string DoNotImplicitlyConvertDateTimeToDateTimeOffset = "MA0132";
public const string UseDateTimeOffsetInsteadOfDateTime = "MA0133";

public static string GetHelpUri(string identifier)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Meziantou.Analyzer.Rules;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class DoNotImplicitlyConvertDateTimeToDateTimeOffsetAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor s_rule = new(
private static readonly DiagnosticDescriptor s_ruleImplicitConversion = new(
RuleIdentifiers.DoNotImplicitlyConvertDateTimeToDateTimeOffset,
title: "Do not convert implicitly to DateTimeOffset",
messageFormat: "Do not convert implicitly to DateTimeOffset",
Expand All @@ -19,7 +19,17 @@ public sealed class DoNotImplicitlyConvertDateTimeToDateTimeOffsetAnalyzer : Dia
description: "",
helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.DoNotImplicitlyConvertDateTimeToDateTimeOffset));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_rule);
private static readonly DiagnosticDescriptor s_ruleUseDateTimeOffset = new(
RuleIdentifiers.UseDateTimeOffsetInsteadOfDateTime,
title: "Use DateTimeOffset instead of relying on the implicit conversion",
messageFormat: "Use DateTimeOffset instead of relying on the implicit conversion",
RuleCategories.Design,
DiagnosticSeverity.Info,
isEnabledByDefault: true,
description: "",
helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.UseDateTimeOffsetInsteadOfDateTime));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_ruleImplicitConversion, s_ruleUseDateTimeOffset);

public override void Initialize(AnalysisContext context)
{
Expand All @@ -46,9 +56,13 @@ private static void AnalyzeConversion(OperationAnalysisContext context, INamedTy
{
// DateTime.Now and DateTime.UtcNow set the DateTime.Kind, so the conversion result is well-known
if (operation.Operand is IMemberReferenceOperation { Member.Name: "UtcNow" or "Now", Member.ContainingType.SpecialType: SpecialType.System_DateTime })
return;

context.ReportDiagnostic(s_rule, operation);
{
context.ReportDiagnostic(s_ruleUseDateTimeOffset, operation);
}
else
{
context.ReportDiagnostic(s_ruleImplicitConversion, operation);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
using Xunit;

namespace Meziantou.Analyzer.Test.Rules;
public class DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests

public class DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0132

{
private static ProjectBuilder CreateProjectBuilder()
{
return new ProjectBuilder()
.WithOutputKind(OutputKind.ConsoleApplication)
.WithAnalyzer<DoNotImplicitlyConvertDateTimeToDateTimeOffsetAnalyzer>();
.WithAnalyzer<DoNotImplicitlyConvertDateTimeToDateTimeOffsetAnalyzer>(id: "MA0132");
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Threading.Tasks;
using Meziantou.Analyzer.Rules;
using Microsoft.CodeAnalysis;
using TestHelper;
using Xunit;

namespace Meziantou.Analyzer.Test.Rules;

public class DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0133
{
private static ProjectBuilder CreateProjectBuilder()
{
return new ProjectBuilder()
.WithOutputKind(OutputKind.ConsoleApplication)
.WithAnalyzer<DoNotImplicitlyConvertDateTimeToDateTimeOffsetAnalyzer>(id: "MA0133");
}

[Fact]
public async Task ImplicitConversion_BinaryOperation_Subtract_UtcNow()
{
await CreateProjectBuilder()
.WithSourceCode("""
using System;
_ = [|DateTime.UtcNow|] - DateTimeOffset.UtcNow;
""")
.ValidateAsync();
}
}

0 comments on commit df707da

Please sign in to comment.