Skip to content

Commit

Permalink
Support banning namespaces
Browse files Browse the repository at this point in the history
  • Loading branch information
Bouke committed Apr 14, 2023
1 parent fee402d commit 8469f59
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,4 @@ namespace N
| `string BannedField` | `F:N.BannedType.BannedField`
| `string BannedProperty { get; }` | `P:N.BannedType.BannedProperty`
| `event EventHandler BannedEvent;` | `E:N.BannedType.BannedEvent`
| `namespace N` | `N:N`
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ private static (string ParentName, string SymbolName)? ParseDeclaredId(string id
case 'M': // Methods
case 'P': // Properties
case 'T': // Types
break;
case 'N': // Namespaces
break;
default:
// Documentation comment id must start with E, F, M, N, P or T. Note: we don't support banning full
// namespaces, so we bail in that case as well.
// Documentation comment id must start with E, F, M, N, P or T.
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,37 @@ bool VerifyType(Action<Diagnostic> reportDiagnostic, ITypeSymbol? type, SyntaxNo
return false;
}

foreach (var currentNamespace in GetContainingNamespaces(type))
{
if (IsBannedSymbol(currentNamespace, out entry))
{
reportDiagnostic(
syntaxNode.CreateDiagnostic(
SymbolIsBannedRule,
currentNamespace.ToDisplayString(),
string.IsNullOrWhiteSpace(entry.Message) ? "" : ": " + entry.Message));
return false;
}
}

type = type.ContainingType;
}
while (!(type is null));

return true;

static IEnumerable<INamespaceSymbol> GetContainingNamespaces(ISymbol symbol)
{
INamespaceSymbol? currentNamespace = symbol.ContainingNamespace;

while (currentNamespace is { IsGlobalNamespace: false })
{
foreach (var constituent in currentNamespace.ConstituentNamespaces)
yield return constituent;

currentNamespace = currentNamespace.ContainingNamespace;
}
}
}

bool VerifyTypeArguments(Action<Diagnostic> reportDiagnostic, ITypeSymbol? type, SyntaxNode syntaxNode, out ITypeSymbol? originalDefinition)
Expand Down Expand Up @@ -366,10 +392,23 @@ public BanFileEntry(Compilation compilation, string text, TextSpan span, SourceT
Path = path;

_lazySymbols = new Lazy<ImmutableArray<ISymbol>>(
() => DocumentationCommentId.GetSymbolsForDeclarationId(DeclarationId, compilation));
() => DocumentationCommentId.GetSymbolsForDeclarationId(DeclarationId, compilation)
.SelectMany(ExpandConstituentNamespaces).ToImmutableArray());

static IEnumerable<ISymbol> ExpandConstituentNamespaces(ISymbol symbol)
{
if (symbol is not INamespaceSymbol namespaceSymbol)
{
yield return symbol;
yield break;
}

foreach (var constituent in namespaceSymbol.ConstituentNamespaces)
yield return constituent;
}
}

public Location Location => Location.Create(Path, Span, SourceText.Lines.GetLinePositionSpan(Span));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,159 @@ void M()
await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", ""));
}

[Fact]
public async Task CSharp_BannedNamespace_ConstructorAsync()
{
var source = @"
namespace N
{
class C
{
void M()
{
var c = {|#0:new N.C()|};
}
}
}
";

var bannedText = @"
N:N";

await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", ""));
}

[Fact]
public async Task CSharp_BannedNamespace_Parent_ConstructorAsync()
{
var source = @"
namespace N.NN
{
class C
{
void M()
{
var a = {|#0:new N.NN.C()|};
}
}
}
";

var bannedText = @"
N:N";

await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", ""));
}

[Fact]
public async Task CSharp_BannedNamespace_MethodGroupAsync()
{
var source = @"
namespace N
{
delegate void D();
class C
{
void M()
{
D d = {|#0:M|};
}
}
}
";

var bannedText = @"
N:N";

await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", ""));
}

[Fact]
public async Task CSharp_BannedNamespace_PropertyAsync()
{
var source = @"
namespace N
{
class C
{
public int P { get; set; }
void M()
{
{|#0:P|} = {|#1:P|};
}
}
}
";

var bannedText = @"
N:N";

await VerifyCSharpAnalyzerAsync(source, bannedText,
GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", ""),
GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", ""));
}

[Fact]
public async Task CSharp_BannedNamespace_MethodAsync()
{
var source = @"
namespace N
{
interface I
{
void M();
}
}
class C
{
void M()
{
N.I i = null;
{|#0:i.M()|};
}
}";
var bannedText = @"N:N";

await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", ""));
}

[Fact]
public async Task CSharp_BannedNamespace_TypeOfArgument()
{
var source = @"
namespace N
{
class Banned { }
}
class C
{
void M()
{
var type = {|#0:typeof(N.Banned)|};
}
}
";
var bannedText = @"N:N";
await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", ""));
}

[Fact]
public async Task CSharp_BannedNamespace_Constituent()
{
var source = @"
class C
{
void M()
{
var thread = {|#0:new System.Threading.Thread((System.Threading.ThreadStart)null)|};
}
}
";
var bannedText = @"N:System.Threading";
await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "System.Threading", ""));
}

[Fact]
public async Task CSharp_BannedGenericType_ConstructorAsync()
{
Expand Down

0 comments on commit 8469f59

Please sign in to comment.