5

We have some guidelines, how we want to use our namespaces and there are also access restrictions on them. Because developers are doing this wrong sometimes, we need to analyze these rules. Currently we are doing this with nDepend, which is working good. But the process that someone have to overwatch this, go to the guy who violated these rules and force him to fix it, is very time consuming. So it would be very nice to get instant notice while developing, or at least after building the current changes. This should be a job for a roslyn analyzer.

I've introduced myself into roslyn the past 3 hours, but I'm a bit overwhelmed with the feature list and how they work. Maybe you can give me a hint, how I could achieve what I want.

We are talking about a solution with >1m lines of code and nearly 35000 types. So peformance does matter a lot.

What I want to do:

  1. get the current class
  2. get the namespace of the current class
  3. get all used types with their full name

If I'm able to do this, the rest would be relatively easy. I've played arround with it and maybe I need the current project of the opened class and the compilation. But opening this is very time consuming and therefore the performance would be very ugly.

1
  • Do you have continuous integration server? You should be able to run NDepend (and any other checks, like unit tests) there for each commit. Commented Oct 22, 2016 at 14:56

1 Answer 1

8

A Roslyn analyzer can register a bunch of different code actions, eg. on the "whole file" level, the method, every single syntax node, or symbol. Depending on what you're exactly are trying to analyze, any of those might be applicable for you. Especially, as you indicate, you're concerned about performance. See the AnalysisContext.Register*Action() methods, for possible "hooks" you can add.

To get the things that you want:

1 Get the current class

Basically, with any of those, you should be able to get the current class (if registering syntax node or symbol action), or all declared classes (for example, with registering a compilation action, or syntax tree action). But the most simple option is to register a syntax node analysis for class nodes, you can do that like this:

context.RegisterSyntaxNodeAction(AnalyzeClassNode, SyntaxKind.ClassDeclaration);

Where AnalyzeClassNode is an action to analyze the class declaration. That will receive an additional context (a SyntaxNodeAnalysisContext), which contains the class declaration syntax node.

2 Get the namespace of the current class

For this, you need the semantic model. Let's say you used the RegisterSyntaxNodeAction method, and declared a method AnalyzeClassNode, then in the body, you can do this:

var classNode = context.Node;
var model = context.SemanticModel;
var classSymbol = model.GetDeclaredSymbol(classNode);

And you get the namespace symbol with:

var @namespace = classSymbol.ContainingNamespace;

And .MetadataName will give you the namespace as string.

3 Get all used types with their full name

That's something much more complex, and really depends on what you're trying to achieve here. To really get to something like "all dependent types, or imports". You should traverse the entire class node, get the symbol for every useful node (I have no idea what that would entail), and checking it's namespace, or full metadata name.

Maybe, you can elaborate a little bit more on this, to find out if this is the right approach.


By the way, check out "Learn Roslyn Now", a site with a bunch of tutorials for Roslyn. Specifically, you want to checkout part 3 (for syntax nodes), 7 (for symbols), and 10 (intro to analyzers).

Sign up to request clarification or add additional context in comments.

7 Comments

Thank you for your very good answer! Ich will check the links you gave me and get back, when I know what I will do. I dont know if it helps: We want to restrict access to namespaces in our own code (namespaces beginning with our company name). Except some framework stuff it is only allowed to access the hierarchie up, or till the 3rd level also across, because we have something like [company].[module].[feature]. Everybody is allowed to access features, but not their children. A "on the fly" hint if you brake this, would be very sweet.
Ah, I see. In that situation, you could also just check the imported namespaces, and check that against the namespace of the file, if you only have a single namespace per file. As opposed to using the semantic model (which probably will be much more expensive). Wouldn't that work? Personally, this is something that I'd try to enforce on the assembly level, and using public accessibility for all the types on the third level. However, I can imagine from the numbers you gave, that it won't be possible anymore for your project...
BTW I wasn't able to actually run my code, since I was writing it at my Mac. If there is something wrong with the code, let me know, so I can update the answer. Maybe, I'll be able tomorrow to verify the samples at my work pc.
The challenge with trying to do namespaces syntactically is you might have to deal with nested ones. Also, if you need to support VB.NET there are cases where the syntax tree isn't sufficient to figure out the namespace.
This is probably a good case where you should just use the semantic model, and if it's too slow then revisit. Premature optimization and all that.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.