From b39866e7ea93f62605584bd4d4b8d73cd50131be Mon Sep 17 00:00:00 2001 From: Collin Alpert Date: Mon, 25 Sep 2023 22:45:34 +0200 Subject: [PATCH] Add MakeTypesInternalAnalyzer (#6820) * Add MakeTypesInternalAnalyzer * Add FixAll support * Add header comment. * Add file header * Make all OutputKinds configurable. --- docs/Analyzer Configuration.md | 4 +- .../CSharpMakeTypesInternal.Fixer.cs | 34 + .../CSharpMakeTypesInternal.cs | 49 ++ .../Core/AnalyzerReleases.Unshipped.md | 1 + .../MakeTypesInternal.Fixer.cs | 38 + .../Maintainability/MakeTypesInternal.cs | 65 ++ ...icrosoftCodeQualityAnalyzersResources.resx | 12 + ...rosoftCodeQualityAnalyzersResources.cs.xlf | 20 + ...rosoftCodeQualityAnalyzersResources.de.xlf | 20 + ...rosoftCodeQualityAnalyzersResources.es.xlf | 20 + ...rosoftCodeQualityAnalyzersResources.fr.xlf | 20 + ...rosoftCodeQualityAnalyzersResources.it.xlf | 20 + ...rosoftCodeQualityAnalyzersResources.ja.xlf | 20 + ...rosoftCodeQualityAnalyzersResources.ko.xlf | 20 + ...rosoftCodeQualityAnalyzersResources.pl.xlf | 20 + ...oftCodeQualityAnalyzersResources.pt-BR.xlf | 20 + ...rosoftCodeQualityAnalyzersResources.ru.xlf | 20 + ...rosoftCodeQualityAnalyzersResources.tr.xlf | 20 + ...tCodeQualityAnalyzersResources.zh-Hans.xlf | 20 + ...tCodeQualityAnalyzersResources.zh-Hant.xlf | 20 + .../Microsoft.CodeAnalysis.NetAnalyzers.md | 12 + .../Microsoft.CodeAnalysis.NetAnalyzers.sarif | 38 + src/NetAnalyzers/RulesMissingDocumentation.md | 1 + .../Maintainability/MakeTypesInternalTests.cs | 713 ++++++++++++++++++ .../BasicMakeTypesInternal.Fixer.vb | 43 ++ .../Maintainability/BasicMakeTypesInternal.vb | 41 + .../DiagnosticCategoryAndIdRanges.txt | 4 +- .../Options/AnalyzerOptionsExtensions.cs | 10 +- 28 files changed, 1321 insertions(+), 4 deletions(-) create mode 100644 src/NetAnalyzers/CSharp/Microsoft.CodeQuality.Analyzers/Maintainability/CSharpMakeTypesInternal.Fixer.cs create mode 100644 src/NetAnalyzers/CSharp/Microsoft.CodeQuality.Analyzers/Maintainability/CSharpMakeTypesInternal.cs create mode 100644 src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternal.Fixer.cs create mode 100644 src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternal.cs create mode 100644 src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternalTests.cs create mode 100644 src/NetAnalyzers/VisualBasic/Microsoft.CodeQuality.Analyzers/Maintainability/BasicMakeTypesInternal.Fixer.vb create mode 100644 src/NetAnalyzers/VisualBasic/Microsoft.CodeQuality.Analyzers/Maintainability/BasicMakeTypesInternal.vb diff --git a/docs/Analyzer Configuration.md b/docs/Analyzer Configuration.md index 0b6a0ae03c..7a5433b3db 100644 --- a/docs/Analyzer Configuration.md +++ b/docs/Analyzer Configuration.md @@ -133,7 +133,9 @@ Users can also provide a comma separated list of above option values. For exampl Option Name: `output_kind` -Configurable Rules: [CA2007](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007) +Configurable Rules: +[CA1515](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1515), +[CA2007](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007) Option Values: One or more fields of enum [Microsoft.CodeAnalysis.CompilationOptions.OutputKind](https://learn.microsoft.com/dotnet/api/microsoft.codeanalysis.outputkind) as a comma separated list. diff --git a/src/NetAnalyzers/CSharp/Microsoft.CodeQuality.Analyzers/Maintainability/CSharpMakeTypesInternal.Fixer.cs b/src/NetAnalyzers/CSharp/Microsoft.CodeQuality.Analyzers/Maintainability/CSharpMakeTypesInternal.Fixer.cs new file mode 100644 index 0000000000..8aa2a00380 --- /dev/null +++ b/src/NetAnalyzers/CSharp/Microsoft.CodeQuality.Analyzers/Maintainability/CSharpMakeTypesInternal.Fixer.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Composition; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeQuality.Analyzers.Maintainability; + +namespace Microsoft.CodeQuality.CSharp.Analyzers.Maintainability +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + public sealed class CSharpMakeTypesInternalFixer : MakeTypesInternalFixer + { + protected override SyntaxNode MakeInternal(SyntaxNode node) => + node switch + { + TypeDeclarationSyntax type => MakeMemberInternal(type), + EnumDeclarationSyntax @enum => MakeMemberInternal(@enum), + DelegateDeclarationSyntax @delegate => MakeMemberInternal(@delegate), + _ => throw new NotSupportedException() + }; + + private static SyntaxNode MakeMemberInternal(MemberDeclarationSyntax type) + { + var publicKeyword = type.Modifiers.First(m => m.IsKind(SyntaxKind.PublicKeyword)); + var modifiers = type.Modifiers.Replace(publicKeyword, SyntaxFactory.Token(SyntaxKind.InternalKeyword)); + + return type.WithModifiers(modifiers); + } + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/CSharp/Microsoft.CodeQuality.Analyzers/Maintainability/CSharpMakeTypesInternal.cs b/src/NetAnalyzers/CSharp/Microsoft.CodeQuality.Analyzers/Maintainability/CSharpMakeTypesInternal.cs new file mode 100644 index 0000000000..89abddcc82 --- /dev/null +++ b/src/NetAnalyzers/CSharp/Microsoft.CodeQuality.Analyzers/Maintainability/CSharpMakeTypesInternal.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeQuality.Analyzers.Maintainability; + +namespace Microsoft.CodeQuality.CSharp.Analyzers.Maintainability +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpMakeTypesInternal : MakeTypesInternal + { + protected override ImmutableArray TypeKinds { get; } = + ImmutableArray.Create(SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.InterfaceDeclaration, SyntaxKind.RecordDeclaration); + + protected override SyntaxKind EnumKind { get; } = SyntaxKind.EnumDeclaration; + + protected override ImmutableArray DelegateKinds { get; } = ImmutableArray.Create(SyntaxKind.DelegateDeclaration); + + protected override void AnalyzeTypeDeclaration(SyntaxNodeAnalysisContext context) + { + var type = (TypeDeclarationSyntax)context.Node; + ReportIfPublic(context, type.Modifiers, type.Identifier); + } + + protected override void AnalyzeEnumDeclaration(SyntaxNodeAnalysisContext context) + { + var @enum = (EnumDeclarationSyntax)context.Node; + ReportIfPublic(context, @enum.Modifiers, @enum.Identifier); + } + + protected override void AnalyzeDelegateDeclaration(SyntaxNodeAnalysisContext context) + { + var @delegate = (DelegateDeclarationSyntax)context.Node; + ReportIfPublic(context, @delegate.Modifiers, @delegate.Identifier); + } + + private static void ReportIfPublic(SyntaxNodeAnalysisContext context, SyntaxTokenList modifiers, SyntaxToken identifier) + { + if (modifiers.Any(SyntaxKind.PublicKeyword)) + { + context.ReportDiagnostic(identifier.CreateDiagnostic(Rule)); + } + } + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index c0945d156a..0d9fcab48b 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -5,3 +5,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- CA1514 | Maintainability | Info | AvoidLengthCheckWhenSlicingToEndAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1514) +CA1515 | Maintainability | Disabled | MakeTypesInternal, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1515) diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternal.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternal.Fixer.cs new file mode 100644 index 0000000000..acf8174fde --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternal.Fixer.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; + +namespace Microsoft.CodeQuality.Analyzers.Maintainability +{ + public abstract class MakeTypesInternalFixer : CodeFixProvider + { + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var node = root.FindNode(context.Span); + + var codeAction = CodeAction.Create( + MicrosoftCodeQualityAnalyzersResources.MakeTypesInternalCodeFixTitle, + _ => + { + var newNode = MakeInternal(node); + var newRoot = root.ReplaceNode(node, newNode.WithTriviaFrom(node)); + + return Task.FromResult(context.Document.WithSyntaxRoot(newRoot)); + }, + MicrosoftCodeQualityAnalyzersResources.MakeTypesInternalCodeFixTitle); + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + protected abstract SyntaxNode MakeInternal(SyntaxNode node); + + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(MakeTypesInternal.RuleId); + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternal.cs b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternal.cs new file mode 100644 index 0000000000..91dc865da7 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternal.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeQuality.Analyzers.Maintainability +{ + using static MicrosoftCodeQualityAnalyzersResources; + + public abstract class MakeTypesInternal : DiagnosticAnalyzer + where TSyntaxKind : struct, Enum + { + internal const string RuleId = "CA1515"; + + protected static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + RuleId, + CreateLocalizableResourceString(nameof(MakeTypesInternalTitle)), + CreateLocalizableResourceString(nameof(MakeTypesInternalMessage)), + DiagnosticCategory.Maintainability, + RuleLevel.Disabled, + description: CreateLocalizableResourceString(nameof(MakeTypesInternalDescription)), + isPortedFxCopRule: false, + isDataflowRule: false); + + private static readonly ImmutableHashSet DefaultOutputKinds = + ImmutableHashSet.Create(OutputKind.ConsoleApplication, OutputKind.WindowsApplication, OutputKind.WindowsRuntimeApplication); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(context => + { + var compilation = context.Compilation; + if (context.Compilation.SyntaxTrees.FirstOrDefault() is not { } firstSyntaxTree + || !context.Options.GetOutputKindsOption(Rule, firstSyntaxTree, compilation, DefaultOutputKinds).Contains(compilation.Options.OutputKind)) + { + return; + } + + context.RegisterSyntaxNodeAction(AnalyzeTypeDeclaration, TypeKinds); + context.RegisterSyntaxNodeAction(AnalyzeEnumDeclaration, EnumKind); + context.RegisterSyntaxNodeAction(AnalyzeDelegateDeclaration, DelegateKinds); + }); + } + + protected abstract ImmutableArray TypeKinds { get; } + + protected abstract TSyntaxKind EnumKind { get; } + + protected abstract ImmutableArray DelegateKinds { get; } + + protected abstract void AnalyzeTypeDeclaration(SyntaxNodeAnalysisContext context); + + protected abstract void AnalyzeEnumDeclaration(SyntaxNodeAnalysisContext context); + + protected abstract void AnalyzeDelegateDeclaration(SyntaxNodeAnalysisContext context); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/MicrosoftCodeQualityAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/MicrosoftCodeQualityAnalyzersResources.resx index 7c03802175..8b34419a26 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/MicrosoftCodeQualityAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/MicrosoftCodeQualityAnalyzersResources.resx @@ -1293,4 +1293,16 @@ Type '{0}' directly or indirectly inherits '{1}' without implementing any of '{2}'. Publicly-visible types should implement the generic version to broaden usability. + + Make the public type internal + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + Consider making public types internal + \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.cs.xlf index 09b3940e46..d63c86cbb6 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.cs.xlf @@ -357,6 +357,26 @@ Používat PascalCase pro pojmenované zástupné objekty + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' Použít AttributeUsageAttribute diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.de.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.de.xlf index 7fa117508f..24e2f15e50 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.de.xlf @@ -357,6 +357,26 @@ PascalCase für benannte Platzhalter verwenden + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' "AttributeUsageAttribute" anwenden diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.es.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.es.xlf index a2dcc614d2..c4fe99845c 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.es.xlf @@ -357,6 +357,26 @@ Usar PascalCase para marcadores de posición con nombre + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' Aplicar "AttributeUsageAttribute" diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.fr.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.fr.xlf index 5ac7c704ee..e8431701fe 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.fr.xlf @@ -357,6 +357,26 @@ Utiliser casse Pascal pour les espaces réservés nommés + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' Appliquer 'AttributeUsageAttribute' diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.it.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.it.xlf index f0eb9cd2a7..7766e82aae 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.it.xlf @@ -357,6 +357,26 @@ Usare la notazione Pascal per i segnaposto denominati + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' Applica 'AttributeUsageAttribute' diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ja.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ja.xlf index 5091be37c3..b58d93a6e3 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ja.xlf @@ -357,6 +357,26 @@ 名前付きプレースホルダーに PascalCase を使用してください + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' 'AttributeUsageAttribute' の適用 diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ko.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ko.xlf index 8cffae1fba..b62fe91d5f 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ko.xlf @@ -357,6 +357,26 @@ 명명된 자리 표시자에 대해 PascalCase 사용 + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' 'AttributeUsageAttribute' 적용 diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pl.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pl.xlf index 87ab5c3c24..ee98d90312 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pl.xlf @@ -357,6 +357,26 @@ Używanie języka PascalCase dla nazwanych symboli zastępczych + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' Zastosuj element „AttributeUsageAttribute” diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pt-BR.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pt-BR.xlf index e6e32c1f6f..0122596824 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pt-BR.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.pt-BR.xlf @@ -357,6 +357,26 @@ Usar o PascalCase nos espaços reservados nomeados + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' Aplicar 'AttributeUsageAttribute' diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ru.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ru.xlf index f49ee0198e..2c1811f690 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ru.xlf @@ -357,6 +357,26 @@ Использовать PascalCase для именованных заполнителей + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' Применить "AttributeUsageAttribute" diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.tr.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.tr.xlf index 18fbec8d01..497906cf68 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.tr.xlf @@ -357,6 +357,26 @@ Adlandırılmış yer tutucular için PascalCase kullanın + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' 'AttributeUsageAttribute' uygula diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hans.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hans.xlf index c145d42480..b1d8af775a 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hans.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hans.xlf @@ -357,6 +357,26 @@ 将 PascalCase 用于已命名占位符 + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' 应用 "AttributeUsageAttribute" diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hant.xlf b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hant.xlf index 333ca9e34a..be2991b0b3 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hant.xlf +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.zh-Hant.xlf @@ -357,6 +357,26 @@ 將 PascalCase 用於已命名的預留位置 + + Make the public type internal + Make the public type internal + + + + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + + + + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + Because an application's API isn't typically referenced from outside the assembly, types can be made internal + + + + Consider making public types internal + Consider making public types internal + + Apply 'AttributeUsageAttribute' 套用 'AttributeUsageAttribute' diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index cd5846c4f0..146dc408da 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -924,6 +924,18 @@ An explicit length calculation can be error-prone and can be avoided when slicin |CodeFix|True| --- +## [CA1515](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1515): Consider making public types internal + +Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal. + +|Item|Value| +|-|-| +|Category|Maintainability| +|Enabled|False| +|Severity|Warning| +|CodeFix|True| +--- + ## [CA1700](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1700): Do not name enum values 'Reserved' This rule assumes that an enumeration member that has a name that contains "reserved" is not currently used but is a placeholder to be renamed or removed in a future version. Renaming or removing a member is a breaking change. diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index 067ec2e151..0e10ba0b36 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -126,6 +126,25 @@ ] } }, + "CA1515": { + "id": "CA1515", + "shortDescription": "Consider making public types internal", + "fullDescription": "Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal.", + "defaultLevel": "warning", + "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1515", + "properties": { + "category": "Maintainability", + "isEnabledByDefault": false, + "typeName": "CSharpMakeTypesInternal", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA1802": { "id": "CA1802", "shortDescription": "Use literals where appropriate", @@ -6328,6 +6347,25 @@ ] } }, + "CA1515": { + "id": "CA1515", + "shortDescription": "Consider making public types internal", + "fullDescription": "Unlike a class library, an application's API isn't typically referenced publicly, so types can be marked internal.", + "defaultLevel": "warning", + "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1515", + "properties": { + "category": "Maintainability", + "isEnabledByDefault": false, + "typeName": "BasicMakeTypesInternal", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA1802": { "id": "CA1802", "shortDescription": "Use literals where appropriate", diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index 0587796be1..a0aea3b9a8 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -7,6 +7,7 @@ CA1511 | | Use ArgumentOutOfRangeException throw helper | CA1513 | | Use ObjectDisposedException throw helper | CA1514 | | Avoid redundant length argument | +CA1515 | | Consider making public types internal | CA1856 | | Incorrect usage of ConstantExpected attribute | CA1857 | | A constant is expected for the parameter | CA1862 | | Use the 'StringComparison' method overloads to perform case-insensitive string comparisons | diff --git a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternalTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternalTests.cs new file mode 100644 index 0000000000..da107531b7 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/Maintainability/MakeTypesInternalTests.cs @@ -0,0 +1,713 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeQuality.CSharp.Analyzers.Maintainability.CSharpMakeTypesInternal, + Microsoft.CodeQuality.CSharp.Analyzers.Maintainability.CSharpMakeTypesInternalFixer>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeQuality.VisualBasic.Analyzers.Maintainability.BasicMakeTypesInternal, + Microsoft.CodeQuality.VisualBasic.Analyzers.Maintainability.BasicMakeTypesInternalFixer>; + +namespace Microsoft.CodeQuality.Analyzers.Maintainability.UnitTests +{ + public sealed class MakeTypesInternalTests + { + [Theory] + [MemberData(nameof(NonDiagnosticTriggeringOutputKinds))] + public async Task LibraryCode_NoDiagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, "public class MyService {}"); + + await VerifyVbAsync(outputKind, "Public Class MyService\nEnd Class"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task ApplicationCode_Diagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +public class [|Program|] +{ + public static void Main() {} +}", + @" +internal class Program +{ + public static void Main() {} +}"); + + await VerifyVbAsync(outputKind, @" +Public Class [|Program|] + Public Shared Sub Main() + End Sub +End Class", + @" +Friend Class Program + Public Shared Sub Main() + End Sub +End Class"); + } + + [Theory] + [MemberData(nameof(NonDiagnosticTriggeringOutputKinds))] + public async Task MultipleTypes_LibraryCode_NoDiagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +public class MyTests {} + +internal class MyService {} + +public class MyValidator {}"); + + await VerifyVbAsync(outputKind, @" +Public Class MyTests +End Class + +Friend Class MyService +End Class + +Public Class MyValidator +End Class"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task MultipleTypes_ApplicationCode_Diagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +public class [|Program|] +{ + public static void Main() {} +} + +internal class MyService {} + +public class [|MyValidator|] {}", + @" +internal class Program +{ + public static void Main() {} +} + +internal class MyService {} + +internal class MyValidator {}"); + await VerifyVbAsync(outputKind, @" +Public Class [|Program|] + Public Shared Sub Main() + End Sub +End Class + +Friend Class MyService +End Class + +Public Class [|MyValidator|] +End Class", + @" +Friend Class Program + Public Shared Sub Main() + End Sub +End Class + +Friend Class MyService +End Class + +Friend Class MyValidator +End Class"); + } + + [Theory] + [MemberData(nameof(NonDiagnosticTriggeringOutputKinds))] + public async Task MultipleDifferentTypes_LibraryCode_NoDiagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +public class MyTests {} + +public abstract class MyBaseType {} + +public struct MyValueType {} + +public interface IValidator {} + +public enum Types {}"); + + await VerifyVbAsync(outputKind, @" +Public Class MyTests +End Class + +Public MustInherit Class MyBaseType +End Class + +Public Structure MyValueType +End Structure + +Public Interface IValidator +End Interface + +Public Enum Types + None +End Enum"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task MultipleDifferentTypes_ApplicationCode_Diagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +public class [|Program|] +{ + public static void Main() {} +} + +public abstract class [|MyBaseType|] {} + +public struct [|MyValueType|] {} + +public interface [|IValidator|] {} + +public record [|Person|]; + +public enum [|Types|] {}", + @" +internal class Program +{ + public static void Main() {} +} + +internal abstract class MyBaseType {} + +internal struct MyValueType {} + +internal interface IValidator {} + +internal record Person; + +internal enum Types {}"); + + await VerifyVbAsync(outputKind, @" +Public Class [|Program|] + Public Shared Sub Main() + End Sub +End Class + +Public MustInherit Class [|MyBaseType|] +End Class + +Public Structure [|MyValueType|] +End Structure + +Public Interface [|IValidator|] +End Interface + +Public Enum [|Types|] + None +End Enum", + @" +Friend Class Program + Public Shared Sub Main() + End Sub +End Class + +Friend MustInherit Class MyBaseType +End Class + +Friend Structure MyValueType +End Structure + +Friend Interface IValidator +End Interface + +Friend Enum Types + None +End Enum"); + } + + [Theory] + [InlineData(OutputKind.DynamicallyLinkedLibrary)] + [InlineData(OutputKind.WindowsRuntimeMetadata)] + public Task Records_LibraryCode_NoDiagnostic(OutputKind outputKind) + { + return VerifyCsAsync(outputKind, @"public record Person;"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public Task Records_ApplicationCode_Diagnostic(OutputKind outputKind) + { + return VerifyCsAsync(outputKind, @" +public class [|Program|] +{ + public static void Main() {} +} + +public record [|Person|];", + @" +internal class Program +{ + public static void Main() {} +} + +internal record Person;"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task NoModifier_ApplicationCode_NoDiagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +class Program +{ + public static void Main() {} +} + +class MyService {} + +sealed class MyValidator {}"); + await VerifyVbAsync(outputKind, @" +Class Program + Public Shared Sub Main() + End Sub +End Class + +Class MyService +End Class + +NotInheritable Class MyValidator +End Class"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task MultipleModifiers_ApplicationCode_Diagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +public class [|Program|] +{ + public static void Main() {} +} + +public sealed class [|MyService|] {} + +public abstract partial class [|MyValidator|] {} + +public partial interface [|IValidator|] {}", + @" +internal class Program +{ + public static void Main() {} +} + +internal sealed class MyService {} + +internal abstract partial class MyValidator {} + +internal partial interface IValidator {}"); + await VerifyVbAsync(outputKind, @" +Public Class [|Program|] + Public Shared Sub Main() + End Sub +End Class + +Public NotInheritable Class [|MyService|] +End Class + +Public Partial MustInherit Class [|MyValidator|] +End Class + +Public Partial Interface [|IValidator|] +End Interface", + @" +Friend Class Program + Public Shared Sub Main() + End Sub +End Class + +Friend NotInheritable Class MyService +End Class + +Friend Partial MustInherit Class MyValidator +End Class + +Friend Partial Interface IValidator +End Interface"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task MultipleUnorderedModifiers_ApplicationCode_Diagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +public class [|Program|] +{ + public static void Main() {} +} + +sealed public class [|MyService|] {}", + @" +internal class Program +{ + public static void Main() {} +} + +sealed internal class MyService {}"); + await VerifyVbAsync(outputKind, @" +Public Class [|Program|] + Public Shared Sub Main() + End Sub +End Class + +NotInheritable Public Class [|MyService|] +End Class", + @" +Friend Class Program + Public Shared Sub Main() + End Sub +End Class + +NotInheritable Friend Class MyService +End Class"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task NestedTypes_ApplicationCode_NoDiagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +class Program +{ + public static void Main() {} + + public struct [|MyValueType|] + { + public class [|Nested|] {} + } +}", + @" +class Program +{ + public static void Main() {} + + internal struct MyValueType + { + internal class Nested {} + } +}"); + await VerifyVbAsync(outputKind, @" +Class Program + Public Shared Sub Main() + End Sub + + Public Structure [|MyValueType|] + Public Class [|Nested|] + End Class + End Structure +End Class", + @" +Class Program + Public Shared Sub Main() + End Sub + + Friend Structure MyValueType + Friend Class Nested + End Class + End Structure +End Class"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task ProtectedTypes_ApplicationCode_NoDiagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +class Program +{ + public static void Main() {} + + protected abstract class MyService {} +}"); + await VerifyVbAsync(outputKind, @" +Class Program + Public Shared Sub Main() + End Sub + + Protected MustInherit Class MyService + End Class +End Class"); + } + + [Theory] + [MemberData(nameof(NonDiagnosticTriggeringOutputKinds))] + public async Task Delegates_LibraryCode_NoDiagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +public delegate int GetValue(string s); +public delegate void Print(object x); +public delegate bool Match(); +"); + + await VerifyVbAsync(outputKind, @" +Imports System + +Public Delegate Function GetValue(s As String) As Int32 +Public Delegate Sub Print(x As Object) +Public Delegate Function Match() As Boolean +"); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task Delegates_ApplicationCode_Diagnostic(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, @" +public delegate int [|GetValue|](string s); +public delegate void [|Print|](object x); +public delegate bool [|Match|](); +internal delegate char GetFirst(string s); + +class Program { + public static void Main() {} +}", + @" +internal delegate int GetValue(string s); +internal delegate void Print(object x); +internal delegate bool Match(); +internal delegate char GetFirst(string s); + +class Program { + public static void Main() {} +}"); + + await VerifyVbAsync(outputKind, @" +Imports System + +Public Delegate Function [|GetValue|](s As String) As Int32 +Public Delegate Sub [|Print|](x As Object) +Public Delegate Function [|Match|]() As Boolean +Friend Delegate Function GetFirst(s As String) As Char + +Class Program + Public Shared Sub Main() + End Sub +End Class", + @" +Imports System + +Friend Delegate Function GetValue(s As String) As Int32 +Friend Delegate Sub Print(x As Object) +Friend Delegate Function Match() As Boolean +Friend Delegate Function GetFirst(s As String) As Char + +Class Program + Public Shared Sub Main() + End Sub +End Class"); + } + + [Theory] + [InlineData("DynamicallyLinkedLibrary", OutputKind.DynamicallyLinkedLibrary)] + [InlineData("ConsoleApplication", OutputKind.ConsoleApplication)] + [InlineData("ConsoleApplication,DynamicallyLinkedLibrary", OutputKind.DynamicallyLinkedLibrary)] + [InlineData("WindowsApplication", OutputKind.WindowsApplication)] + [InlineData("WindowsRuntimeApplication", OutputKind.WindowsRuntimeApplication)] + [InlineData("ConsoleApplication,WindowsApplication", OutputKind.WindowsApplication)] + [InlineData("ConsoleApplication,WindowsApplication,WindowsRuntimeApplication", OutputKind.WindowsRuntimeApplication)] + public async Task CompilationOptions_Diagnostic(string optionsText, OutputKind outputKind) + { + await new VerifyCS.Test + { + TestCode = """ + public class [|Program|] + { + public static void Main() {} + } + """, + FixedCode = """ + internal class Program + { + public static void Main() {} + } + """, + TestState = + { + OutputKind = outputKind, + AnalyzerConfigFiles = + { + ("/.editorconfig", + $""" + root = true + + [*] + dotnet_code_quality.CA1515.output_kind = {optionsText} + """) + } + }, + LanguageVersion = LanguageVersion.CSharp10 + }.RunAsync(); + + await new VerifyVB.Test + { + TestCode = """ + Public Class [|Program|] + Public Shared Sub Main() + End Sub + End Class + """, + FixedCode = """ + Friend Class Program + Public Shared Sub Main() + End Sub + End Class + """, + TestState = + { + OutputKind = outputKind, + AnalyzerConfigFiles = + { + ("/.editorconfig", + $""" + root = true + + [*] + dotnet_code_quality.CA1515.output_kind = {optionsText} + """) + } + }, + }.RunAsync(); + } + + [Theory] + [InlineData("ConsoleApplication", OutputKind.DynamicallyLinkedLibrary)] + [InlineData("DynamicallyLinkedLibrary", OutputKind.ConsoleApplication)] + [InlineData("WindowsApplication", OutputKind.ConsoleApplication)] + [InlineData("WindowsRuntimeApplication", OutputKind.ConsoleApplication)] + [InlineData("ConsoleApplication,WindowsApplication", OutputKind.WindowsRuntimeApplication)] + [InlineData("ConsoleApplication,WindowsApplication,WindowsRuntimeApplication", OutputKind.DynamicallyLinkedLibrary)] + public async Task CompilationOptions_NoDiagnostic(string optionsText, OutputKind outputKind) + { + await new VerifyCS.Test + { + TestCode = """ + public class Program + { + public static void Main() {} + } + """, + TestState = + { + OutputKind = outputKind, + AnalyzerConfigFiles = + { + ("/.editorconfig", + $""" + root = true + + [*] + dotnet_code_quality.CA1515.output_kind = {optionsText} + """) + } + }, + LanguageVersion = LanguageVersion.CSharp10 + }.RunAsync(); + + await new VerifyVB.Test + { + TestCode = """ + Public Class Program + Public Shared Sub Main() + End Sub + End Class + """, + TestState = + { + OutputKind = outputKind, + AnalyzerConfigFiles = + { + ("/.editorconfig", + $""" + root = true + + [*] + dotnet_code_quality.CA1515.output_kind = {optionsText} + """) + } + }, + }.RunAsync(); + } + + [Theory] + [MemberData(nameof(DiagnosticTriggeringOutputKinds))] + public async Task Trivia(OutputKind outputKind) + { + await VerifyCsAsync(outputKind, + """ + // This is the entry point class. + public class [|Program|] + { + public static void Main() {} + } + """, + """ + // This is the entry point class. + internal class Program + { + public static void Main() {} + } + """); + + await VerifyVbAsync(outputKind, + """ + ' This is the entry point class. + Public Class [|Program|] + Public Shared Sub Main() + End Sub + End Class + """, + """ + ' This is the entry point class. + Friend Class Program + Public Shared Sub Main() + End Sub + End Class + """); + } + + private Task VerifyCsAsync(OutputKind outputKind, string testCode, string fixedCode = null) + { + return new VerifyCS.Test + { + TestCode = testCode, + FixedCode = fixedCode!, + TestState = { OutputKind = outputKind }, + LanguageVersion = LanguageVersion.CSharp10 + }.RunAsync(); + } + + private Task VerifyVbAsync(OutputKind outputKind, string testCode, string fixedCode = null) + { + return new VerifyVB.Test + { + TestCode = testCode, + FixedCode = fixedCode!, + TestState = { OutputKind = outputKind } + }.RunAsync(); + } + + public static IEnumerable DiagnosticTriggeringOutputKinds() + { + yield return new object[] { OutputKind.ConsoleApplication }; + yield return new object[] { OutputKind.WindowsRuntimeApplication }; + yield return new object[] { OutputKind.WindowsApplication }; + } + + public static IEnumerable NonDiagnosticTriggeringOutputKinds() + { + yield return new object[] { OutputKind.NetModule }; + yield return new object[] { OutputKind.DynamicallyLinkedLibrary }; + yield return new object[] { OutputKind.WindowsRuntimeMetadata }; + } + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/VisualBasic/Microsoft.CodeQuality.Analyzers/Maintainability/BasicMakeTypesInternal.Fixer.vb b/src/NetAnalyzers/VisualBasic/Microsoft.CodeQuality.Analyzers/Maintainability/BasicMakeTypesInternal.Fixer.vb new file mode 100644 index 0000000000..d7211df683 --- /dev/null +++ b/src/NetAnalyzers/VisualBasic/Microsoft.CodeQuality.Analyzers/Maintainability/BasicMakeTypesInternal.Fixer.vb @@ -0,0 +1,43 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +Imports System.Composition +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.CodeQuality.Analyzers.Maintainability + +Namespace Microsoft.CodeQuality.VisualBasic.Analyzers.Maintainability + + Public Class BasicMakeTypesInternalFixer + Inherits MakeTypesInternalFixer + + Protected Overrides Function MakeInternal(node As SyntaxNode) As SyntaxNode + Dim type = TryCast(node, TypeStatementSyntax) + If type IsNot Nothing + Dim publicKeyword = type.Modifiers.First(Function(m) m.IsKind(SyntaxKind.PublicKeyword)) + Dim modifiers = type.Modifiers.Replace(publicKeyword, SyntaxFactory.Token(SyntaxKind.FriendKeyword)) + + Return type.WithModifiers(modifiers) + End If + + Dim enumStatement = TryCast(node, EnumStatementSyntax) + If enumStatement IsNot Nothing + Dim publicKeyword = enumStatement.Modifiers.First(Function(m) m.IsKind(SyntaxKind.PublicKeyword)) + Dim modifiers = enumStatement.Modifiers.Replace(publicKeyword, SyntaxFactory.Token(SyntaxKind.FriendKeyword)) + + Return enumStatement.WithModifiers(modifiers) + End If + + Dim delegateStatement = TryCast(node, DelegateStatementSyntax) + If delegateStatement IsNot Nothing + Dim publicKeyword = delegateStatement.Modifiers.First(Function(m) m.IsKind(SyntaxKind.PublicKeyword)) + Dim modifiers = delegateStatement.Modifiers.Replace(publicKeyword, SyntaxFactory.Token(SyntaxKind.FriendKeyword)) + + Return delegateStatement.WithModifiers(modifiers) + End If + + Throw New NotSupportedException() + End Function + End Class +End Namespace \ No newline at end of file diff --git a/src/NetAnalyzers/VisualBasic/Microsoft.CodeQuality.Analyzers/Maintainability/BasicMakeTypesInternal.vb b/src/NetAnalyzers/VisualBasic/Microsoft.CodeQuality.Analyzers/Maintainability/BasicMakeTypesInternal.vb new file mode 100644 index 0000000000..f71e2050bd --- /dev/null +++ b/src/NetAnalyzers/VisualBasic/Microsoft.CodeQuality.Analyzers/Maintainability/BasicMakeTypesInternal.vb @@ -0,0 +1,41 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +Imports System.Collections.Immutable +Imports Analyzer.Utilities.Extensions +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.CodeQuality.Analyzers.Maintainability + +Namespace Microsoft.CodeQuality.VisualBasic.Analyzers.Maintainability + + Public NotInheritable Class BasicMakeTypesInternal + Inherits MakeTypesInternal(Of SyntaxKind) + + Protected Overrides ReadOnly Property TypeKinds As ImmutableArray(Of SyntaxKind) = ImmutableArray.Create(SyntaxKind.ClassStatement, SyntaxKind.StructureStatement, SyntaxKind.InterfaceStatement) + Protected Overrides ReadOnly Property EnumKind As SyntaxKind = SyntaxKind.EnumStatement + Protected Overrides ReadOnly Property DelegateKinds As ImmutableArray(Of SyntaxKind) = ImmutableArray.Create(SyntaxKind.DelegateFunctionStatement, SyntaxKind.DelegateSubStatement) + + Protected Overrides Sub AnalyzeTypeDeclaration(context As SyntaxNodeAnalysisContext) + Dim type = DirectCast(context.Node, TypeStatementSyntax) + ReportIfPublic(context, type.Modifiers, type.Identifier) + End Sub + + Protected Overrides Sub AnalyzeEnumDeclaration(context As SyntaxNodeAnalysisContext) + Dim enumStatement = DirectCast(context.Node, EnumStatementSyntax) + ReportIfPublic(context, enumStatement.Modifiers, enumStatement.Identifier) + End Sub + + Protected Overrides Sub AnalyzeDelegateDeclaration(context As SyntaxNodeAnalysisContext) + Dim delegateStatement = DirectCast(context.Node, DelegateStatementSyntax) + ReportIfPublic(context, delegateStatement.Modifiers, delegateStatement.Identifier) + End Sub + + Private Shared Sub ReportIfPublic(context As SyntaxNodeAnalysisContext, modifiers As SyntaxTokenList, identifier As SyntaxToken) + If modifiers.Any(SyntaxKind.PublicKeyword) Then + context.ReportDiagnostic(identifier.CreateDiagnostic(Rule)) + End If + End Sub + End Class +End Namespace \ No newline at end of file diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index 38330d36cb..2423ad707e 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -1,4 +1,4 @@ -# This file contains the allowed analyzer rule "Category" and corresponding "Diagnostic ID range" +# This file contains the allowed analyzer rule "Category" and corresponding "Diagnostic ID range" # FORMAT: # 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' @@ -17,7 +17,7 @@ Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5405 Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2261 Naming: CA1700-CA1727 Interoperability: CA1400-CA1422 -Maintainability: CA1500-CA1514 +Maintainability: CA1500-CA1515 Reliability: CA9998-CA9999, CA2000-CA2021 Documentation: CA1200-CA1200 diff --git a/src/Utilities/Compiler/Options/AnalyzerOptionsExtensions.cs b/src/Utilities/Compiler/Options/AnalyzerOptionsExtensions.cs index 6905b5e50d..c3fc07b0e8 100644 --- a/src/Utilities/Compiler/Options/AnalyzerOptionsExtensions.cs +++ b/src/Utilities/Compiler/Options/AnalyzerOptionsExtensions.cs @@ -95,7 +95,15 @@ public static ImmutableHashSet GetOutputKindsOption( DiagnosticDescriptor rule, SyntaxTree tree, Compilation compilation) - => options.GetNonFlagsEnumOptionValue(EditorConfigOptionNames.OutputKind, rule, tree, compilation, s_defaultOutputKinds); + => options.GetOutputKindsOption(rule, tree, compilation, s_defaultOutputKinds); + + public static ImmutableHashSet GetOutputKindsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + ImmutableHashSet defaultValue) + => options.GetNonFlagsEnumOptionValue(EditorConfigOptionNames.OutputKind, rule, tree, compilation, defaultValue); public static ImmutableHashSet GetAnalyzedSymbolKindsOption( this AnalyzerOptions options,