Skip to content

Instantly share code, notes, and snippets.

@b0urb4k1
Last active July 10, 2017 18:39
Show Gist options
  • Select an option

  • Save b0urb4k1/a88c35eeced8d22634dc8914ea0aa5cb to your computer and use it in GitHub Desktop.

Select an option

Save b0urb4k1/a88c35eeced8d22634dc8914ea0aa5cb to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using static Microsoft.CodeAnalysis.Diagnostic;
namespace Analyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class NamespaceAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "NamespaceAnalyzer";
private const string LocalTitle = "Namespace Problem";
private const string MessageFormat = "Namespace {0} is inaccessable from {1}, extra text {2}.";
private const string Description = "Namespace Checker";
private const string Category = "Access Rights";
private static readonly DiagnosticDescriptor ShowLocalContainingNamespace =
new DiagnosticDescriptor
( DiagnosticId
, LocalTitle
, MessageFormat
, Category
, DiagnosticSeverity.Error
, isEnabledByDefault: true
, description: Description
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(ShowLocalContainingNamespace);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnaylzeSyntax, SyntaxKind.IdentifierName);
}
private void AnaylzeSyntax(SyntaxNodeAnalysisContext context)
{
var syntaxNode = context.Node;
var semanticModel = context.SemanticModel;
var cancellationToken = context.CancellationToken;
var namespaces = GetNamespaces(syntaxNode, semanticModel, cancellationToken);
var additionFiles = context.Options.AdditionalFiles;
if (namespaces.HasValue && NamespaceBlocked(namespaces.Value))
context.ReportDiagnostic
( Create
( ShowLocalContainingNamespace
, syntaxNode.GetLocation()
, namespaces.Value.Item2
, namespaces.Value.Item1
, additionFiles[0].GetText()
)
);
}
private static Optional<Tuple<string, string>> GetNamespaces(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
{
try
{
var localNode = node as IdentifierNameSyntax;
if (localNode.IsVar == false)
{
var localSymbol =
localNode
.Ancestors()
.Where(n => n != node)
.Select(n => semanticModel.GetDeclaredSymbol(n))
.First(s => s != null);
var localNamespace = localSymbol.ContainingNamespace;
var declaringSymbol = semanticModel.GetSymbolInfo(localNode).Symbol;
var declaringNamespace = declaringSymbol.ContainingNamespace;
return new Optional<Tuple<string, string>>
( Tuple.Create
( NamespaceName(localNamespace)
, NamespaceName(declaringNamespace)
)
);
}
}
catch
{
return default(Optional<Tuple<string, string>>);
}
return default(Optional<Tuple<string, string>>);
}
private static bool NamespaceBlocked(Tuple<string, string> namespaces)
=> NamespaceBlocked(namespaces.Item1, namespaces.Item2);
private static bool NamespaceBlocked(string local, string declaring) =>
declaring == "Other" && local != "Other";
private static string NamespaceName(INamespaceSymbol namespaceSymbol)
=> string.Join(".", GetFullNamespace(namespaceSymbol, new string[] { }));
private static string[] GetFullNamespace(INamespaceSymbol namespaceSymbol, string[] parts) =>
(namespaceSymbol == null || namespaceSymbol.IsGlobalNamespace)
? parts
: GetFullNamespace
( namespaceSymbol.ContainingNamespace
, parts.Concat(Enumerable.Repeat(namespaceSymbol.Name, 1)).ToArray()
);
}
}
@jmarolf
Copy link
Copy Markdown

jmarolf commented Sep 22, 2016

@b0urb4k1 A few questions:

  1. What is this analyzer supposed to do?
  2. It will be very expensive to get every symbol for all identifiers. Do you really want all identifiers? Ideally the semantic model should be used after we've done some work syntactically to verify that we have found a specific case we want to analyze with the semantic apis.

@b0urb4k1
Copy link
Copy Markdown
Author

Hey sorry i just saw your comment.
The idea is that you can prevent the usage of symbols from one namespace in another namespace.
Thus you would be able to enforce a layered architecture.
Since it is so long ago i am not sure anymore why i did not choose the semantic model but i think there was some issue that forced me to not use it. I just found this project here which intends to do exactly what i had in mind https://github.com/realvizu/NsDepCop . But i did not get it to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment