From 25f60c3f1d156f0a76d51a4bb78ea1e42891ac46 Mon Sep 17 00:00:00 2001 From: Jesse Squire Date: Wed, 14 Feb 2024 11:06:24 -0800 Subject: [PATCH] [.NET Analyzers] Fix Template special case (#7683) * [.NET Analyzers] Fix Template special case The focus of these changes is to move the template special case logic from an equality check to a `StartsWith`, matching the behavior of the other checks. --- .../AZC0001Tests.cs | 73 +++++++++++++++++-- .../ClientAssemblyNamespaceAnalyzer.cs | 5 +- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/dotnet/Azure.ClientSdk.Analyzers/Azure.ClientSdk.Analyzers.Tests/AZC0001Tests.cs b/src/dotnet/Azure.ClientSdk.Analyzers/Azure.ClientSdk.Analyzers.Tests/AZC0001Tests.cs index 5296fd1e7e6..575ccfce44c 100644 --- a/src/dotnet/Azure.ClientSdk.Analyzers/Azure.ClientSdk.Analyzers.Tests/AZC0001Tests.cs +++ b/src/dotnet/Azure.ClientSdk.Analyzers/Azure.ClientSdk.Analyzers.Tests/AZC0001Tests.cs @@ -33,17 +33,33 @@ public class Program { } } [Fact] - public async Task AZC0001ProducedForSubNamespacesOfAzureTemplate() + public async Task AZC0001ProducedForInvalidNamespaceWithValidRoot() { const string code = @" -namespace Azure.Template.RandomNamespace +namespace Azure.StorageBadNamespace +{ + public class Program { } +}"; + + var diagnostic = Verifier.Diagnostic("AZC0001") + .WithMessage(string.Format(this.message, "Azure.StorageBadNamespace")) + .WithSpan(2, 17, 2, 36); + + await Verifier.VerifyAnalyzerAsync(code, diagnostic); + } + + [Fact] + public async Task AZC0001ProducedForInvalidSubNamespaceWithValidRoot() + { + const string code = @" +namespace Azure.StorageBadNamespace.Child { public class Program { } }"; var diagnostic = Verifier.Diagnostic("AZC0001") - .WithMessage(string.Format(this.message, "Azure.Template.RandomNamespace")) - .WithSpan(2, 26, 2, 41); + .WithMessage(string.Format(this.message, "Azure.StorageBadNamespace.Child")) + .WithSpan(2, 37, 2, 42); await Verifier.VerifyAnalyzerAsync(code, diagnostic); } @@ -79,6 +95,17 @@ internal class Program { } public async Task AZC0001NotProducedForAllowedNamespaces() { const string code = @" +namespace Azure.Storage +{ + public class Program { } +}"; + await Verifier.VerifyAnalyzerAsync(code); + } + + [Fact] + public async Task AZC0001NotProducedForAllowedSubNamespaces() + { + const string code = @" namespace Azure.Storage.Hello { public class Program { } @@ -90,6 +117,18 @@ public class Program { } public async Task AZC0001NotProducedForAzureCoreExpressions() { const string code = @" +namespace Azure.Core.Expressions +{ + public class Program { } +}"; + + await Verifier.VerifyAnalyzerAsync(code); + } + + [Fact] + public async Task AZC0001NotProducedForAzureCoreExpressionsSubNamespace() + { + const string code = @" namespace Azure.Core.Expressions.Foobar { public class Program { } @@ -99,10 +138,34 @@ public class Program { } } [Fact] - public async Task AZC0001NotProducedForAzureTemplate() + public async Task AZC0001NotProducedForSubNamespacesOfAzureTemplate() + { + const string code = @" +namespace Azure.Template.RandomNamespace +{ + public class Program { } +}"; + + await Verifier.VerifyAnalyzerAsync(code); + } + + [Fact] + public async Task AZC0001NotProducedForAzureTemplateRoot() { const string code = @" namespace Azure.Template +{ + public class Program { } +}"; + + await Verifier.VerifyAnalyzerAsync(code); + } + + [Fact] + public async Task AZC0001NotProducedForAzureTemplateSubNamespace() + { + const string code = @" +namespace Azure.Template.Models { public class Program { } }"; diff --git a/src/dotnet/Azure.ClientSdk.Analyzers/Azure.ClientSdk.Analyzers/ClientAssemblyNamespaceAnalyzer.cs b/src/dotnet/Azure.ClientSdk.Analyzers/Azure.ClientSdk.Analyzers/ClientAssemblyNamespaceAnalyzer.cs index 355be34f7ba..866774063f8 100644 --- a/src/dotnet/Azure.ClientSdk.Analyzers/Azure.ClientSdk.Analyzers/ClientAssemblyNamespaceAnalyzer.cs +++ b/src/dotnet/Azure.ClientSdk.Analyzers/Azure.ClientSdk.Analyzers/ClientAssemblyNamespaceAnalyzer.cs @@ -70,7 +70,8 @@ public override void Analyze(ISymbolAnalysisContext context) var displayString = namespaceSymbol.ToDisplayString(); foreach (var prefix in AllowedNamespacePrefix) { - if (displayString.StartsWith(prefix)) + // Both the namespace itself or a sub-namespace are valid. + if (displayString == prefix || displayString.StartsWith(prefix + ".")) { return; } @@ -78,7 +79,7 @@ public override void Analyze(ISymbolAnalysisContext context) // "Azure.Template" is not an approved namespace prefix, but we have a project template by that name // to help customers get started. We do not want our template to include a suppression for this // descriptor out of the box, so we need to treat it as a special case. - if (displayString == "Azure.Template") + if (displayString == "Azure.Template" || displayString.StartsWith("Azure.Template.")) { return; }