Skip to content

Instantly share code, notes, and snippets.

@icalvo
Last active August 29, 2015 13:56
Show Gist options
  • Save icalvo/9235756 to your computer and use it in GitHub Desktop.
Save icalvo/9235756 to your computer and use it in GitHub Desktop.
Class for collecting calls to given targets in your C# source code, using Roslyn.
namespace Icm
{
using System.Collections.Generic;
using System.Linq;
using Roslyn.Compilers.CSharp;
/// <summary>
/// This syntax walker collects all the calls to the specified targets.
/// </summary>
public class CallsCollector : SyntaxWalker
{
private readonly IEnumerable<string> targets;
private readonly IList<MemberAccessExpressionSyntax> calls;
/// <summary>
/// Initializes a new instance of the <see cref="CallsCollector"/> class.
/// </summary>
/// <param name="targets">The targets.</param>
public CallsCollector(params string[] targets)
{
this.calls = new List<MemberAccessExpressionSyntax>();
this.targets = targets;
}
/// <summary>
/// Gets the calls found to the specified targets.
/// </summary>
/// <value>
/// The calls found to the specified targets.
/// </value>
public IEnumerable<MemberAccessExpressionSyntax> Calls
{
get
{
return this.calls;
}
}
/// <summary>
/// Determines whether the specified target is called.
/// </summary>
/// <param name="target">The target.</param>
/// <returns></returns>
public bool IsCalled(string target)
{
return this.calls.Any(call => ((IdentifierNameSyntax)call.Expression).Identifier.ValueText == target);
}
/// <summary>
/// Called when the visitor visits a MemberAccessExpressionSyntax node.
/// </summary>
/// <param name="node">The node.</param>
public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
var identifierName = node.Expression as IdentifierNameSyntax;
if (identifierName != null)
{
if (this.targets.Contains(identifierName.Identifier.ValueText))
{
this.calls.Add(node);
}
}
}
}
}
namespace Icm
{
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Roslyn.Compilers.CSharp;
[TestClass]
public class ConventionsTests
{
/// <summary>
/// Tests that there are no calls to File or Directory in the code.
/// </summary>
[TestMethod]
public void FileDirectoryCallsForbidden()
{
var solutionRootPath = Path.GetFullPath(@"..\..\..");
foreach (string sourceFileName in Directory.GetFiles(solutionRootPath, "*.cs", SearchOption.AllDirectories))
{
SyntaxTree tree = SyntaxTree.ParseFile(sourceFileName);
CompilationUnitSyntax root = tree.GetRoot();
var collector = new CallsCollector("File", "Directory");
collector.Visit(root);
Assert.IsFalse(collector.IsCalled("File"), "System.IO.File is called at " + sourceFileName.Substring(solutionRootPath.Length) + ". Please use an IFileSystem dependency instead.");
Assert.IsFalse(collector.IsCalled("Directory"), "System.IO.Directory is called at " + sourceFileName.Substring(solutionRootPath.Length) + ". Please use an IFileSystem dependency instead.");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment