Reputation: 8442
Does anybody know of a way to prevent developers from using a particular type of expression in a Visual Studio C# code base? I have seen that analysers might be the way to go here.
I would like Visual Studio to warn developers when they use DateTime.Now
instead of DateTime.UtcNow
in our code base.
As far as my analysis goes, I don't think we will ever need to use DateTime.Now
in our API code base, so I'm also pondering the idea of throwing errors at compilation if DateTime.Now
is used, in case unit testing doesn't cover this case.
The reason I want to do this is to prevent any non UTC time values from making their way into the database. Senior developers are solid on this, however I have seen time and time again DateTime.Now
being used by less senior developers, and I would like to automate the management of this.
I know that setting server time zones to UTC could negate this, however I'd like to solve the issue closer to the cause.
Are there any existing tools out there I could leverage, that anyone knows of, or should I write my own analyser?
Any tips would be much appreciated.
Upvotes: 2
Views: 420
Reputation: 897
Here's a simple Rosalyn example:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; //NuGet package
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
namespace StackOverflow_RosalynExample
{
class Program
{
static void Main(string[] args)
{
var tree = CSharpSyntaxTree.ParseText(@"
using System;
namespace SomeNamespace
{
class SomeClass
{
public void SomeMethod()
{
DateTime example = DateTime.Now;
}
}
}");
var rewriter = new DateTimeUtcEnsurer();
var result = rewriter.Visit(tree.GetRoot());
Console.WriteLine(result.ToFullString());
Console.ReadKey(); //DateTime.Now -> DateTime.UtcNow
}
}
public class DateTimeUtcEnsurer : CSharpSyntaxRewriter
{
public override SyntaxNode VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
var dateTimeNow = SyntaxFactory.ParseExpression("DateTime.Now") as MemberAccessExpressionSyntax;
if (SyntaxFactory.AreEquivalent(node, dateTimeNow))
{
var dateTimeUtcNow = SyntaxFactory.ParseExpression("DateTime.UtcNow") as MemberAccessExpressionSyntax;
dateTimeUtcNow = dateTimeUtcNow.WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia(" /*Silly junior dev ;)*/"));
return dateTimeUtcNow;
}
return base.VisitMemberAccessExpression(node);
}
}
}
This is just to give you an idea of how to use Roslyn, a C# compiler tool. Here, I use it to convert a string to a C# Abstract Syntax Tree, modify the AST directly, and then toString the AST.
The big advantage of manipulating the AST over, say directly manipulating text via regex, is safety. (Admittedly less important in this example).
For your needs, you could adjust this example to accept a file directory as a command line argument and run the rewriter against all files ending in cs. (I did something similar last year; ~10,000 files in ~20 seconds). Then, you could run the enforcement tool before each check-in to source control.
Alternatively, you could make an active code-analyzer, a la intellisense. (Roslyn is probably more geared for that anyways.)
Good luck!
Upvotes: 1