diff --git a/analyzers/src/SonarAnalyzer.CSharp.Styling/Common/StylingAnalyzer.cs b/analyzers/src/SonarAnalyzer.CSharp.Styling/Common/StylingAnalyzer.cs new file mode 100644 index 00000000000..1f116b6e7ba --- /dev/null +++ b/analyzers/src/SonarAnalyzer.CSharp.Styling/Common/StylingAnalyzer.cs @@ -0,0 +1,31 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2024 SonarSource SA + * mailto: contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarAnalyzer.CSharp.Styling.Common; + +public abstract class StylingAnalyzer : SonarDiagnosticAnalyzer +{ + protected DiagnosticDescriptor Rule { get; } + + public sealed override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + protected StylingAnalyzer(string id, string messageFormat, SourceScope scope = SourceScope.All) => + Rule = DescriptorFactory.Create(id, messageFormat, scope); +} diff --git a/analyzers/src/SonarAnalyzer.CSharp.Styling/Rules/FileScopeNamespace.cs b/analyzers/src/SonarAnalyzer.CSharp.Styling/Rules/FileScopeNamespace.cs index 670f0a0919b..a948d99b259 100644 --- a/analyzers/src/SonarAnalyzer.CSharp.Styling/Rules/FileScopeNamespace.cs +++ b/analyzers/src/SonarAnalyzer.CSharp.Styling/Rules/FileScopeNamespace.cs @@ -21,18 +21,13 @@ namespace SonarAnalyzer.Rules.CSharp.Styling; [DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class FileScopeNamespace : SonarDiagnosticAnalyzer // ToDo: https://github.com/SonarSource/sonar-dotnet/issues/9035 Extract into a usefull base class +public sealed class FileScopeNamespace : StylingAnalyzer { - private static readonly DiagnosticDescriptor Rule = DescriptorFactory.Create("T0001", "Use file-scoped namespaces"); + public FileScopeNamespace() : base("T0001", "Use file-scoped namespace.") { } - public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); // ToDo: https://github.com/SonarSource/sonar-dotnet/issues/9035 Remove this - - protected override void Initialize(SonarAnalysisContext context) - { - // ToDo: https://github.com/SonarSource/sonar-dotnet/issues/9035 - if (context is null) - { - return; // This is a useless coverage test for now. It will be removed - } - } + protected override void Initialize(SonarAnalysisContext context) => + // ToDo: Rework reporting + context.RegisterNodeAction( + c => c.ReportIssue(Diagnostic.Create(Rule, ((NamespaceDeclarationSyntax)c.Node).Name.GetLocation())), + SyntaxKind.NamespaceDeclaration); } diff --git a/analyzers/src/SonarAnalyzer.CSharp.Styling/SonarAnalyzer.CSharp.Styling.csproj b/analyzers/src/SonarAnalyzer.CSharp.Styling/SonarAnalyzer.CSharp.Styling.csproj index b1af81a6656..03d11459f83 100644 --- a/analyzers/src/SonarAnalyzer.CSharp.Styling/SonarAnalyzer.CSharp.Styling.csproj +++ b/analyzers/src/SonarAnalyzer.CSharp.Styling/SonarAnalyzer.CSharp.Styling.csproj @@ -9,9 +9,9 @@ - + - + diff --git a/analyzers/src/SonarAnalyzer.CSharp.Styling/packages.lock.json b/analyzers/src/SonarAnalyzer.CSharp.Styling/packages.lock.json index ddd5d6998a8..2f076af6052 100644 --- a/analyzers/src/SonarAnalyzer.CSharp.Styling/packages.lock.json +++ b/analyzers/src/SonarAnalyzer.CSharp.Styling/packages.lock.json @@ -941,6 +941,14 @@ "Microsoft.Composition": "[1.0.27, )", "System.Collections.Immutable": "[1.1.37, )" } + }, + "sonaranalyzer.csharp": { + "type": "Project", + "dependencies": { + "Microsoft.CodeAnalysis.CSharp.Workspaces": "[1.3.2, )", + "SonarAnalyzer": "[1.0.0, )", + "System.Collections.Immutable": "[1.1.37, )" + } } } } diff --git a/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/Rules/FileScopeNamespaceTest.cs b/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/Rules/FileScopeNamespaceTest.cs index 366d3363a7c..75ef87668d2 100644 --- a/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/Rules/FileScopeNamespaceTest.cs +++ b/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/Rules/FileScopeNamespaceTest.cs @@ -23,7 +23,13 @@ namespace SonarAnalyzer.CSharp.Styling.Test.Rules; [TestClass] public class FileScopeNamespaceTest { + private readonly VerifierBuilder builder = new VerifierBuilder().WithOptions(ParseOptionsHelper.CSharpLatest).WithConcurrentAnalysis(false); + [TestMethod] public void FileScopeNamespace() => - new VerifierBuilder().WithOptions(ParseOptionsHelper.CSharpLatest).AddPaths("FileScopeNamespace.cs").Verify(); + builder.AddPaths("FileScopeNamespace.cs").Verify(); + + [TestMethod] + public void FileScopeNamespace_Compliant() => + builder.AddPaths("FileScopeNamespace.Compliant.cs").VerifyNoIssueReported(); } diff --git a/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/TestCases/FileScopeNamespace.Compliant.cs b/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/TestCases/FileScopeNamespace.Compliant.cs new file mode 100644 index 00000000000..36b6a331738 --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/TestCases/FileScopeNamespace.Compliant.cs @@ -0,0 +1,6 @@ +namespace Outer; // Compliant + +public class NotRelevant +{ + +} diff --git a/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/TestCases/FileScopeNamespace.cs b/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/TestCases/FileScopeNamespace.cs index 72817e450c2..ce00663307e 100644 --- a/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/TestCases/FileScopeNamespace.cs +++ b/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/TestCases/FileScopeNamespace.cs @@ -1 +1,20 @@ -// FIXME: Rewrite this file in https://github.com/SonarSource/sonar-dotnet/issues/9035 \ No newline at end of file + +namespace Outer // Noncompliant {{Use file-scoped namespace.}} +// ^^^^^ +{ + public class NotRelevant + { + + } + + namespace Inner // Noncompliant, nested namespace should not be used in general + { + + } +} + + +namespace // Error [CS1001] Identifier expected +{ // Noncompliant^-1#0 + +} diff --git a/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/packages.lock.json b/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/packages.lock.json index 5d0e3bd9405..7ca3e64af48 100644 --- a/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/packages.lock.json +++ b/analyzers/tests/SonarAnalyzer.CSharp.Styling.Test/packages.lock.json @@ -552,11 +552,20 @@ "System.Collections.Immutable": "[1.1.37, )" } }, + "sonaranalyzer.csharp": { + "type": "Project", + "dependencies": { + "Microsoft.CodeAnalysis.CSharp.Workspaces": "[1.3.2, )", + "SonarAnalyzer": "[1.0.0, )", + "System.Collections.Immutable": "[1.1.37, )" + } + }, "sonaranalyzer.csharp.styling": { "type": "Project", "dependencies": { "Microsoft.CodeAnalysis.CSharp.Workspaces": "[1.3.2, )", - "SonarAnalyzer": "[1.0.0, )" + "SonarAnalyzer": "[1.0.0, )", + "SonarAnalyzer.CSharp": "[1.0.0, )" } }, "sonaranalyzer.testframework": {