2

I've implemented a TimeProvider within a large C# application, so that there is centralized control over the application time. I want to make sure in the future that no one is using DateTime.Now/Today/UtcNow as this would circumvent the previous mentioned implementation. Instead, I want an analyzer to suggest to use my TimeProviderService, so that it is not missed in e.g a code review.

I want to solve this with an analyzer, so therefore my question is: How do I do this?

Below is one of my attemps; however, this one seems to trigger some sort of legacy issue with FxCop:

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class Analyzer : DiagnosticAnalyzer
{

    public override void Initialize(AnalysisContext context)
    {
        context.RegisterSyntaxNodeAction(AnalyzeSymbol, SyntaxKind.IdentifierName);
    }

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }

    public static string DiagnosticId = "1337";

    private static void AnalyzeSymbol(SyntaxNodeAnalysisContext context)
    {
        var token = context.Node.GetFirstToken();
        if (token.ToString().Equals("DateTime"))
        {
            var nextToken = token.GetNextToken();

            if (nextToken.IsKind(SyntaxKind.DotToken))
            {
                var dateTimeProperty = nextToken.GetNextToken();

                var descripter = new DiagnosticDescriptor(DiagnosticId, "DateTime.Today: Use the TimeProvider.Today instead", "", "Noitso best practise", DiagnosticSeverity.Error, isEnabledByDefault: true);

                if (dateTimeProperty.ToString().Equals("Now"))
                {
                    context.ReportDiagnostic(Diagnostic.Create(descripter, context.Node.GetLocation(), "DateTime.Now: Use the TimeProvider.Now instead"));
                }
                else if (dateTimeProperty.ToString().Equals("Today"))
                {

                    context.ReportDiagnostic(Diagnostic.Create(descripter, context.Node.GetLocation()));
                }
                else if (dateTimeProperty.ToString().Equals("UtcNow"))
                {
                    context.ReportDiagnostic(Diagnostic.Create(descripter, context.Node.GetLocation(), "DateTime.UtcNow: Use the TimeProvider.UtcNow instead"));
                }
            }
        }
    }
}
Seyb
  • 39
  • 7
  • how does your code look now? You'll also probably never get away from people coding `DateTime.Now` etc, but have a look at something like this https://stackoverflow.com/questions/43711/whats-a-good-way-to-overwrite-datetime-now-during-testing – Simon Price Nov 21 '19 at 08:24
  • I've tried solving this issue over a period of time, so I can't remember all my attempts. Today I was looking at this [post](https://stackoverflow.com/questions/32076495/how-can-i-find-previous-usages-of-a-variable-using-roslyn?noredirect=1&lq=1) as a reference, but then I realized I want a value that is specifically linked to the DateTime class. Also it seems that it is out of date (something with not using FxCorp). The solution will give me all all instances of timeservice as well (since it is TimeService.Now) I'll admit, I'm really green at Roslyn, and it seems very overwhelming – Seyb Nov 21 '19 at 08:27
  • Sorry, I accidently deleted my comment. @Simon Price - That is exactly why I want to use a code analyzer, because I can trigger a rule that will not let it build if they do :)! Thank you for the link as well - it seems that the accepted answer is exactly what I am doing. – Seyb Nov 21 '19 at 08:29

1 Answers1

0

I recreated my project, which solved the legacy issue. I have no idea what I did wrong when I created it the first time :).

The code almost worked, as it had one flaw var dateTimeProperty = token.GetNextToken(); is referencing the wrong token, it should be referencing nextToken.

Seyb
  • 39
  • 7