Introduce new InternalsVisibleToAnalyzer analyzer (ACZ0112) with `FriendAttribute` concept (#7086)

This commit is contained in:
Christopher Scott 2024-01-23 10:27:04 -06:00 коммит произвёл GitHub
Родитель adc439e21b
Коммит 7787c1b232
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
12 изменённых файлов: 826 добавлений и 289 удалений

Просмотреть файл

@ -12,7 +12,8 @@
<IsTestProject Condition="'$(IsTestHelperLibrary)' == 'true'">false</IsTestProject>
</PropertyGroup>
<PropertyGroup Condition="'$(SignAssembly)' != 'false'">
<!-- Exclude TestReferenceWithInternalsVisibleTo from strong name signing, as it is required for a test scenario -->
<PropertyGroup Condition="'$(SignAssembly)' != 'false' and !$(MSBuildProjectName.EndsWith('TestReferenceWithInternalsVisibleTo'))">
<SignAssembly>true</SignAssembly>
<DelaySign>false</DelaySign>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)AzureSDKToolsKey.snk</AssemblyOriginatorKeyFile>

Просмотреть файл

@ -0,0 +1,234 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
using Verifier = Azure.ClientSdk.Analyzers.Tests.AzureAnalyzerVerifier<Azure.ClientSdk.Analyzers.InternalsVisibleToAnalyzer>;
namespace Azure.ClientSdk.Analyzers.Tests
{
public class AZC0112Tests
{
[Fact]
public async Task AZC0020WhenInheritingFromInternalInterface()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
namespace LibraryNamespace
{
public class {|AZC0112:MyClass|} : IInternalInterface
{
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task NoAZC0020WhenInheritingFromInternalInterfaceWithFriendAttribute()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
namespace LibraryNamespace
{
public class MyClass : IInternalInterfaceWithFriendAttribute
{
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task AZC0020WhenDerivingFromInternalClass()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
namespace LibraryNamespace
{
internal class {|AZC0112:MyClass|} : InternalClass
{
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task NoAZC0020WhenInheritingFromInternalClassWithFriendAttribute()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
namespace LibraryNamespace
{
internal class MyClass : InternalClassWithFriendAttribute
{
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task AZC0020WhenDeclaringInternalProperty()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
namespace LibraryNamespace
{
public class MyClass
{
internal InternalClass {|AZC0112:PropReferencesInternalType|} { {|AZC0112:get|}; set;}
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task NoAZC0020WhenDeclaringInternalPropertyWithFriendAttribute()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
namespace LibraryNamespace
{
public class MyClass
{
internal InternalClassWithFriendAttribute PropReferencesInternalType { get; set;}
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task NoAZC0020WhenDeclaringInternalFieldWithFriendAttribute()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
namespace LibraryNamespace
{
public class MyClass
{
internal InternalClassWithFriendAttribute fieldReferencesInternalType;
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task AZC0020WhenDeclaringInternalField()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
namespace LibraryNamespace
{
public class MyClass
{
internal InternalClass {|AZC0112:fieldReferencesInternalType|};
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task AZC0020WhenReferencingInternalProperty()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
using System.Reflection;
namespace LibraryNamespace
{
public class MyClass
{
public void MyMethod()
{
var myClass = new PublicClass();
var value = {|AZC0112:myClass.InternalProperty|};
}
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task NoAZC0020WhenReferencingInternalPropertyWithFriendAttribute()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
using System.Reflection;
namespace LibraryNamespace
{
public class MyClass
{
public void MyMethod()
{
var myClass = new PublicClass();
var value = myClass.InternalPropertyWithFriendAttribute;
}
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task AZC0020WhenReferencingInternalMethod()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
using System.Reflection;
namespace LibraryNamespace
{
public class MyClass
{
public void MyMethod()
{
var myClass = new PublicClass();
{|AZC0112:myClass.InternalMethod|}();
}
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
[Fact]
public async Task NoAZC0020WhenReferencingInternalMethodWithFriendAttribute()
{
string code = @"
using System;
using TestReferenceWithInternalsVisibleTo;
using System.Reflection;
namespace LibraryNamespace
{
public class MyClass
{
public void MyMethod()
{
var myClass = new PublicClass();
myClass.InternalMethodWithFriendAttribute();
}
}
}";
await Verifier.VerifyAnalyzerAsync(code, additionalReferences: new[] { typeof(TestReferenceWithInternalsVisibleTo.PublicClass) });
}
}
}

Просмотреть файл

@ -8,6 +8,7 @@
<ItemGroup>
<ProjectReference Include="..\Azure.ClientSdk.Analyzers\Azure.ClientSdk.Analyzers.csproj" />
<ProjectReference Include="..\TestReferenceWithInternalsVisibleTo\TestReferenceWithInternalsVisibleTo.csproj" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
@ -18,14 +19,15 @@ namespace Azure.ClientSdk.Analyzers.Tests
{
private static readonly ReferenceAssemblies DefaultReferenceAssemblies =
ReferenceAssemblies.Default.AddPackages(ImmutableArray.Create(
new PackageIdentity("Azure.Core", "1.26.0"),
new PackageIdentity("Microsoft.Bcl.AsyncInterfaces", "1.1.0"),
new PackageIdentity("Azure.Core", "1.35.0"),
new PackageIdentity("Microsoft.Bcl.AsyncInterfaces", "1.1.1"),
new PackageIdentity("Newtonsoft.Json", "12.0.3"),
new PackageIdentity("System.Text.Json", "4.6.0"),
new PackageIdentity("System.Threading.Tasks.Extensions", "4.5.3")));
new PackageIdentity("System.Text.Json", "4.7.2"),
new PackageIdentity("System.Threading.Tasks.Extensions", "4.5.4")));
public static CSharpAnalyzerTest<TAnalyzer, XUnitVerifier> CreateAnalyzer(string source, LanguageVersion languageVersion = LanguageVersion.Latest)
=> new CSharpAnalyzerTest<TAnalyzer, XUnitVerifier>
public static CSharpAnalyzerTest<TAnalyzer, XUnitVerifier> CreateAnalyzer(string source, LanguageVersion languageVersion = LanguageVersion.Latest, Type[] additionalReferences = null)
{
var test = new CSharpAnalyzerTest<TAnalyzer, XUnitVerifier>
{
ReferenceAssemblies = DefaultReferenceAssemblies,
SolutionTransforms = {(solution, projectId) =>
@ -37,9 +39,18 @@ namespace Azure.ClientSdk.Analyzers.Tests
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck
};
if (additionalReferences != null)
{
foreach (var reference in additionalReferences)
{
test.TestState.AdditionalReferences.Add(reference.Assembly);
}
}
return test;
}
public static Task VerifyAnalyzerAsync(string source, LanguageVersion languageVersion = LanguageVersion.Latest)
=> CreateAnalyzer(source, languageVersion).RunAsync(CancellationToken.None);
public static Task VerifyAnalyzerAsync(string source, LanguageVersion languageVersion = LanguageVersion.Latest, Type[] additionalReferences = null)
=> CreateAnalyzer(source, languageVersion, additionalReferences).RunAsync(CancellationToken.None);
public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] diagnostics)
{

Просмотреть файл

@ -1,57 +1,85 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33103.201
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.ClientSdk.Analyzers", "Azure.ClientSdk.Analyzers\Azure.ClientSdk.Analyzers.csproj", "{0C85C003-716E-415A-850F-D0597DACA658}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.ClientSdk.Analyzers.Tests", "Azure.ClientSdk.Analyzers.Tests\Azure.ClientSdk.Analyzers.Tests.csproj", "{53ABAABB-3B1C-493B-88FB-B1E22A95D232}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{65E960CB-422D-47FB-BEFD-A88B3474CD86}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
ci.yml = ci.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|x64.ActiveCfg = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|x64.Build.0 = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|x86.ActiveCfg = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|x86.Build.0 = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|Any CPU.Build.0 = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|x64.ActiveCfg = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|x64.Build.0 = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|x86.ActiveCfg = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|x86.Build.0 = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|x64.ActiveCfg = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|x64.Build.0 = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|x86.ActiveCfg = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|x86.Build.0 = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|Any CPU.Build.0 = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|x64.ActiveCfg = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|x64.Build.0 = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|x86.ActiveCfg = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F28E9386-1FF2-4372-A91A-88B507A36B23}
EndGlobalSection
EndGlobal

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33103.201
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.ClientSdk.Analyzers", "Azure.ClientSdk.Analyzers\Azure.ClientSdk.Analyzers.csproj", "{0C85C003-716E-415A-850F-D0597DACA658}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.ClientSdk.Analyzers.Tests", "Azure.ClientSdk.Analyzers.Tests\Azure.ClientSdk.Analyzers.Tests.csproj", "{53ABAABB-3B1C-493B-88FB-B1E22A95D232}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{65E960CB-422D-47FB-BEFD-A88B3474CD86}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
ci.yml = ci.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestReferenceWithInternalsVisibleTo", "TestReferenceWithInternalsVisibleTo\TestReferenceWithInternalsVisibleTo.csproj", "{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|arm64 = Debug|arm64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|arm64 = Release|arm64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|arm64.ActiveCfg = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|arm64.Build.0 = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|x64.ActiveCfg = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|x64.Build.0 = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|x86.ActiveCfg = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Debug|x86.Build.0 = Debug|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|Any CPU.Build.0 = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|arm64.ActiveCfg = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|arm64.Build.0 = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|x64.ActiveCfg = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|x64.Build.0 = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|x86.ActiveCfg = Release|Any CPU
{0C85C003-716E-415A-850F-D0597DACA658}.Release|x86.Build.0 = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|arm64.ActiveCfg = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|arm64.Build.0 = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|x64.ActiveCfg = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|x64.Build.0 = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|x86.ActiveCfg = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Debug|x86.Build.0 = Debug|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|Any CPU.Build.0 = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|arm64.ActiveCfg = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|arm64.Build.0 = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|x64.ActiveCfg = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|x64.Build.0 = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|x86.ActiveCfg = Release|Any CPU
{53ABAABB-3B1C-493B-88FB-B1E22A95D232}.Release|x86.Build.0 = Release|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Debug|arm64.ActiveCfg = Debug|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Debug|arm64.Build.0 = Debug|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Debug|x64.ActiveCfg = Debug|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Debug|x64.Build.0 = Debug|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Debug|x86.ActiveCfg = Debug|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Debug|x86.Build.0 = Debug|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Release|Any CPU.Build.0 = Release|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Release|arm64.ActiveCfg = Release|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Release|arm64.Build.0 = Release|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Release|x64.ActiveCfg = Release|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Release|x64.Build.0 = Release|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Release|x86.ActiveCfg = Release|Any CPU
{7FC92CF2-C6CB-4BAF-9397-D26713EF728C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F28E9386-1FF2-4372-A91A-88B507A36B23}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -1,141 +1,141 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.CodeAnalysis;
namespace Azure.ClientSdk.Analyzers
{
internal class Descriptors
{
private static readonly string AZC0001Title = "Use one of the following pre-approved namespace groups (https://azure.github.io/azure-sdk/registered_namespaces.html): " + string.Join(", ", ClientAssemblyNamespaceAnalyzer.AllowedNamespacePrefix);
#region Guidelines
public static DiagnosticDescriptor AZC0001 = new DiagnosticDescriptor(
nameof(AZC0001), AZC0001Title,
"Namespace '{0}' shouldn't contain public types. " + AZC0001Title, DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0002 = new DiagnosticDescriptor(
nameof(AZC0002),
"DO ensure all service methods, both asynchronous and synchronous, take an optional CancellationToken parameter called 'cancellationToken' or a RequestContext parameter called 'context'.",
"Client method should have an optional CancellationToken called cancellationToken (both name and it being optional matters) or a RequestContext called context as the last parameter.",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-cancellation"
);
public static DiagnosticDescriptor AZC0003 = new DiagnosticDescriptor(
nameof(AZC0003),
"DO make service methods virtual.",
"DO make service methods virtual.",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-virtual"
);
public static DiagnosticDescriptor AZC0004 = new DiagnosticDescriptor(
nameof(AZC0004),
"DO provide both asynchronous and synchronous variants for all service methods.",
"DO provide both asynchronous and synchronous variants for all service methods.",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-sync-and-async"
);
public static DiagnosticDescriptor AZC0005 = new DiagnosticDescriptor(
nameof(AZC0005),
"DO provide protected parameterless constructor for mocking.",
"DO provide protected parameterless constructor for mocking.",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-client-constructor-for-mocking"
);
public static DiagnosticDescriptor AZC0006 = new DiagnosticDescriptor(
nameof(AZC0006),
"DO provide constructor overloads that allow specifying additional options.",
"A client type should have a public constructor with equivalent parameters that takes a Azure.Core.ClientOptions-derived type as the last argument",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-client-constructor-overloads"
);
public static DiagnosticDescriptor AZC0007 = new DiagnosticDescriptor(
nameof(AZC0007),
"DO provide a minimal constructor that takes only the parameters required to connect to the service.",
"A client type should have a public constructor with equivalent parameters that doesn't take a Azure.Core.ClientOptions-derived type as the last argument",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-client-constructor-minimal"
);
public static DiagnosticDescriptor AZC0008 = new DiagnosticDescriptor(
nameof(AZC0008), "ClientOptions should have a nested enum called ServiceVersion",
"Client type should have a nested enum called ServiceVersion", DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0009 = new DiagnosticDescriptor(
nameof(AZC0009), "ClientOptions constructors should take a ServiceVersion as their first parameter",
"ClientOptions constructors should take a ServiceVersion as their first parameter. Default constructor should be overloaded to provide ServiceVersion.", DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0010 = new DiagnosticDescriptor(
nameof(AZC0010), "ClientOptions constructors should default ServiceVersion to latest supported service version",
"ClientOptions constructors should default ServiceVersion to latest supported service version", DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0011 = new DiagnosticDescriptor(
nameof(AZC0011), "Avoid InternalsVisibleTo to non-test assemblies",
"Internal visible to product libraries effectively become public API and have to be versioned appropriately", DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0012 = new DiagnosticDescriptor(
nameof(AZC0012), "Avoid single word type names",
"Single word class names are too generic and have high chance of collision with BCL types or types from other libraries",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0013 = new DiagnosticDescriptor(
nameof(AZC0013),
"Use TaskCreationOptions.RunContinuationsAsynchronously when instantiating TaskCompletionSource",
"All the tasks continuations are executed synchronously unless TaskCreationOptions.RunContinuationsAsynchronously option is specified. This may cause deadlocks and other threading issues if all \"async\" continuations have to run in the thread that sets the result of a task.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0014 = new DiagnosticDescriptor(
nameof(AZC0014),
"Avoid using banned types in public API",
"Types from {0} assemblies should not be exposed as part of public API surface.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0015 = new DiagnosticDescriptor(
nameof(AZC0015),
"Unexpected client method return type.",
"Client methods should return Pageable<T>/AsyncPageable<T>/Operation<T>/Task<Operation<T>>/Response/Response<T>/Task<Response>/Task<Response<T>> or other client class found {0} instead.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0016 = new DiagnosticDescriptor(
nameof(AZC0016),
"Invalid ServiceVersion member name.",
"All parts of ServiceVersion members' names must begin with a number or uppercase letter and cannot have consecutive underscores.",
"Usage",
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.CodeAnalysis;
namespace Azure.ClientSdk.Analyzers
{
internal class Descriptors
{
private static readonly string AZC0001Title = "Use one of the following pre-approved namespace groups (https://azure.github.io/azure-sdk/registered_namespaces.html): " + string.Join(", ", ClientAssemblyNamespaceAnalyzer.AllowedNamespacePrefix);
#region Guidelines
public static DiagnosticDescriptor AZC0001 = new DiagnosticDescriptor(
nameof(AZC0001), AZC0001Title,
"Namespace '{0}' shouldn't contain public types. " + AZC0001Title, DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0002 = new DiagnosticDescriptor(
nameof(AZC0002),
"DO ensure all service methods, both asynchronous and synchronous, take an optional CancellationToken parameter called 'cancellationToken' or a RequestContext parameter called 'context'.",
"Client method should have an optional CancellationToken called cancellationToken (both name and it being optional matters) or a RequestContext called context as the last parameter.",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-cancellation"
);
public static DiagnosticDescriptor AZC0003 = new DiagnosticDescriptor(
nameof(AZC0003),
"DO make service methods virtual.",
"DO make service methods virtual.",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-virtual"
);
public static DiagnosticDescriptor AZC0004 = new DiagnosticDescriptor(
nameof(AZC0004),
"DO provide both asynchronous and synchronous variants for all service methods.",
"DO provide both asynchronous and synchronous variants for all service methods.",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-sync-and-async"
);
public static DiagnosticDescriptor AZC0005 = new DiagnosticDescriptor(
nameof(AZC0005),
"DO provide protected parameterless constructor for mocking.",
"DO provide protected parameterless constructor for mocking.",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-client-constructor-for-mocking"
);
public static DiagnosticDescriptor AZC0006 = new DiagnosticDescriptor(
nameof(AZC0006),
"DO provide constructor overloads that allow specifying additional options.",
"A client type should have a public constructor with equivalent parameters that takes a Azure.Core.ClientOptions-derived type as the last argument",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-client-constructor-overloads"
);
public static DiagnosticDescriptor AZC0007 = new DiagnosticDescriptor(
nameof(AZC0007),
"DO provide a minimal constructor that takes only the parameters required to connect to the service.",
"A client type should have a public constructor with equivalent parameters that doesn't take a Azure.Core.ClientOptions-derived type as the last argument",
DiagnosticCategory.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null,
"https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-client-constructor-minimal"
);
public static DiagnosticDescriptor AZC0008 = new DiagnosticDescriptor(
nameof(AZC0008), "ClientOptions should have a nested enum called ServiceVersion",
"Client type should have a nested enum called ServiceVersion", DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0009 = new DiagnosticDescriptor(
nameof(AZC0009), "ClientOptions constructors should take a ServiceVersion as their first parameter",
"ClientOptions constructors should take a ServiceVersion as their first parameter. Default constructor should be overloaded to provide ServiceVersion.", DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0010 = new DiagnosticDescriptor(
nameof(AZC0010), "ClientOptions constructors should default ServiceVersion to latest supported service version",
"ClientOptions constructors should default ServiceVersion to latest supported service version", DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0011 = new DiagnosticDescriptor(
nameof(AZC0011), "Avoid InternalsVisibleTo to non-test assemblies",
"Internal visible to product libraries effectively become public API and have to be versioned appropriately", DiagnosticCategory.Usage, DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0012 = new DiagnosticDescriptor(
nameof(AZC0012), "Avoid single word type names",
"Single word class names are too generic and have high chance of collision with BCL types or types from other libraries",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0017 = new DiagnosticDescriptor(
nameof(AZC0017),
"Invalid convenience method signature.",
"Convenience methods shouldn't have parameters with the RequestContent type.",
"Usage", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null);
public static DiagnosticDescriptor AZC0018 = new DiagnosticDescriptor(
nameof(AZC0018),
"Invalid protocol method signature.",
"Protocol methods should take a RequestContext parameter called `context` and not use a model type in a parameter or return type.",
"Usage", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null);
public static DiagnosticDescriptor AZC0019 = new DiagnosticDescriptor(
nameof(AZC0019),
"Potential ambiguous call exists.",
"There will be an ambiguous call error when the user calls with only the required parameters. All parameters of the protocol method should be required.",
public static DiagnosticDescriptor AZC0013 = new DiagnosticDescriptor(
nameof(AZC0013),
"Use TaskCreationOptions.RunContinuationsAsynchronously when instantiating TaskCompletionSource",
"All the tasks continuations are executed synchronously unless TaskCreationOptions.RunContinuationsAsynchronously option is specified. This may cause deadlocks and other threading issues if all \"async\" continuations have to run in the thread that sets the result of a task.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0014 = new DiagnosticDescriptor(
nameof(AZC0014),
"Avoid using banned types in public API",
"Types from {0} assemblies should not be exposed as part of public API surface.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0015 = new DiagnosticDescriptor(
nameof(AZC0015),
"Unexpected client method return type.",
"Client methods should return Pageable<T>/AsyncPageable<T>/Operation<T>/Task<Operation<T>>/Response/Response<T>/Task<Response>/Task<Response<T>> or other client class found {0} instead.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0016 = new DiagnosticDescriptor(
nameof(AZC0016),
"Invalid ServiceVersion member name.",
"All parts of ServiceVersion members' names must begin with a number or uppercase letter and cannot have consecutive underscores.",
"Usage",
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0017 = new DiagnosticDescriptor(
nameof(AZC0017),
"Invalid convenience method signature.",
"Convenience methods shouldn't have parameters with the RequestContent type.",
"Usage", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null);
public static DiagnosticDescriptor AZC0020 = new DiagnosticDescriptor(
nameof(AZC0020),
"Avoid using banned types in public APIs",
"The Azure.Core internal shared source types {0} should not be used outside of the Azure.Core library.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0018 = new DiagnosticDescriptor(
nameof(AZC0018),
"Invalid protocol method signature.",
"Protocol methods should take a RequestContext parameter called `context` and not use a model type in a parameter or return type.",
"Usage", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null);
public static DiagnosticDescriptor AZC0019 = new DiagnosticDescriptor(
nameof(AZC0019),
"Potential ambiguous call exists.",
"There will be an ambiguous call error when the user calls with only the required parameters. All parameters of the protocol method should be required.",
"Usage", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: null);
public static DiagnosticDescriptor AZC0020 = new DiagnosticDescriptor(
nameof(AZC0020),
"Avoid using banned types in public APIs",
"The Azure.Core internal shared source types {0} should not be used outside of the Azure.Core library.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static readonly DiagnosticDescriptor AZC0030 = new DiagnosticDescriptor(
nameof(AZC0030),
@ -172,92 +172,99 @@ namespace Azure.ClientSdk.Analyzers
DiagnosticSeverity.Warning,
true,
"Suffix is not recommended. Consider to remove or modify it.");
#endregion
#region General
public static DiagnosticDescriptor AZC0100 = new DiagnosticDescriptor(
nameof(AZC0100),
"ConfigureAwait(false) must be used.",
"ConfigureAwait(false) must be used.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0101 = new DiagnosticDescriptor(
nameof(AZC0101),
"Use ConfigureAwait(false) instead of ConfigureAwait(true).",
"Use ConfigureAwait(false) instead of ConfigureAwait(true).",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0102 = new DiagnosticDescriptor(
nameof(AZC0102),
"Do not use GetAwaiter().GetResult().",
"Do not use GetAwaiter().GetResult(). Use the TaskExtensions.EnsureCompleted() extension method instead.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0103 = new DiagnosticDescriptor(
nameof(AZC0103),
"Do not wait synchronously in asynchronous scope.",
"Do not use {0} in asynchronous scope. Use await keyword instead.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0104 = new DiagnosticDescriptor(
nameof(AZC0104),
"Use EnsureCompleted() directly on asynchronous method return value.",
"Don't use {0}. Call EnsureCompleted() extension method directly on the return value of the asynchronous method that has 'bool async' parameter.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0105 = new DiagnosticDescriptor(
nameof(AZC0105),
"DO NOT add 'async' parameter to public methods.",
"DO provide both asynchronous and synchronous variants for all service methods instead of one variant with 'async' parameter.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0106 = new DiagnosticDescriptor(
nameof(AZC0106),
"Non-public asynchronous method needs 'async' parameter.",
"Non-public asynchronous method that is called in synchronous scope should have a boolean 'async' parameter.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0107 = new DiagnosticDescriptor(
nameof(AZC0107),
"DO NOT call public asynchronous method in synchronous scope.",
"Public asynchronous method shouldn't be called in synchronous scope. Use synchronous version of the method if it is available.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0108 = new DiagnosticDescriptor(
nameof(AZC0108),
"Incorrect 'async' parameter value.",
"In {0} scope 'async' parameter for the '{1}' method call should {2}.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0109 = new DiagnosticDescriptor(
nameof(AZC0109),
"Misuse of 'async' parameter.",
"'async' parameter in asynchronous method can't be changed and can only be used as an exclusive condition in '?:' operator or conditional statement.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0110 = new DiagnosticDescriptor(
nameof(AZC0110),
"DO NOT use await keyword in possibly synchronous scope.",
"Asynchronous method with `async` parameter can be called from both synchronous and asynchronous scopes. 'await' keyword can be safely used either in guaranteed asynchronous scope (i.e. `if (async) {...}`) or if `async` parameter is passed into awaited method. Awaiting on variables, fields, properties, conditional operators or async methods that don't use `async` parameter isn't allowed outside of the guaranteed asynchronous scope.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0111 = new DiagnosticDescriptor(
nameof(AZC0111),
"DO NOT use EnsureCompleted in possibly asynchronous scope.",
"Asynchronous method with `async` parameter can be called from both synchronous and asynchronous scopes. 'EnsureCompleted' extension method can be safely used on in guaranteed synchronous scope (i.e. `if (!async) {...}`).",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
#endregion
}
}
#endregion
#region General
public static DiagnosticDescriptor AZC0100 = new DiagnosticDescriptor(
nameof(AZC0100),
"ConfigureAwait(false) must be used.",
"ConfigureAwait(false) must be used.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0101 = new DiagnosticDescriptor(
nameof(AZC0101),
"Use ConfigureAwait(false) instead of ConfigureAwait(true).",
"Use ConfigureAwait(false) instead of ConfigureAwait(true).",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0102 = new DiagnosticDescriptor(
nameof(AZC0102),
"Do not use GetAwaiter().GetResult().",
"Do not use GetAwaiter().GetResult(). Use the TaskExtensions.EnsureCompleted() extension method instead.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0103 = new DiagnosticDescriptor(
nameof(AZC0103),
"Do not wait synchronously in asynchronous scope.",
"Do not use {0} in asynchronous scope. Use await keyword instead.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0104 = new DiagnosticDescriptor(
nameof(AZC0104),
"Use EnsureCompleted() directly on asynchronous method return value.",
"Don't use {0}. Call EnsureCompleted() extension method directly on the return value of the asynchronous method that has 'bool async' parameter.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0105 = new DiagnosticDescriptor(
nameof(AZC0105),
"DO NOT add 'async' parameter to public methods.",
"DO provide both asynchronous and synchronous variants for all service methods instead of one variant with 'async' parameter.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0106 = new DiagnosticDescriptor(
nameof(AZC0106),
"Non-public asynchronous method needs 'async' parameter.",
"Non-public asynchronous method that is called in synchronous scope should have a boolean 'async' parameter.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0107 = new DiagnosticDescriptor(
nameof(AZC0107),
"DO NOT call public asynchronous method in synchronous scope.",
"Public asynchronous method shouldn't be called in synchronous scope. Use synchronous version of the method if it is available.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0108 = new DiagnosticDescriptor(
nameof(AZC0108),
"Incorrect 'async' parameter value.",
"In {0} scope 'async' parameter for the '{1}' method call should {2}.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0109 = new DiagnosticDescriptor(
nameof(AZC0109),
"Misuse of 'async' parameter.",
"'async' parameter in asynchronous method can't be changed and can only be used as an exclusive condition in '?:' operator or conditional statement.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0110 = new DiagnosticDescriptor(
nameof(AZC0110),
"DO NOT use await keyword in possibly synchronous scope.",
"Asynchronous method with `async` parameter can be called from both synchronous and asynchronous scopes. 'await' keyword can be safely used either in guaranteed asynchronous scope (i.e. `if (async) {...}`) or if `async` parameter is passed into awaited method. Awaiting on variables, fields, properties, conditional operators or async methods that don't use `async` parameter isn't allowed outside of the guaranteed asynchronous scope.",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0111 = new DiagnosticDescriptor(
nameof(AZC0111),
"DO NOT use EnsureCompleted in possibly asynchronous scope.",
"Asynchronous method with `async` parameter can be called from both synchronous and asynchronous scopes. 'EnsureCompleted' extension method can be safely used on in guaranteed synchronous scope (i.e. `if (!async) {...}`).",
DiagnosticCategory.Usage,
DiagnosticSeverity.Warning, true);
public static DiagnosticDescriptor AZC0112 = new DiagnosticDescriptor(
"AZC0112",
"Misuse of internal type via [InternalsVisibleTo].",
"{0} is defined in assembly {1} and is marked internal without a [Friend] attribute.",
"Naming",
DiagnosticSeverity.Warning, true);
#endregion
}
}

Просмотреть файл

@ -0,0 +1,182 @@
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Azure.ClientSdk.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class InternalsVisibleToAnalyzer : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptors.AZC0112);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
// Register for symbol actions
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Property);
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Field);
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Parameter);
context.RegisterSyntaxNodeAction(AnalyzeMemberAccess, SyntaxKind.SimpleMemberAccessExpression);
}
private static void AnalyzeSymbol(SymbolAnalysisContext context)
{
var symbol = context.Symbol;
if (symbol is INamedTypeSymbol namedTypeSymbol)
{
AnalysisNamedType(namedTypeSymbol, context);
}
else if (symbol is IMethodSymbol methodSymbol)
{
AnalysisMethod(methodSymbol, context);
}
else if (symbol is IPropertySymbol propertySymbol)
{
AnalysisProperty(propertySymbol, context);
}
else if (symbol is IFieldSymbol fieldSymbol)
{
AnalysisField(fieldSymbol, context);
}
else if (symbol is IParameterSymbol parameterSymbol)
{
AnalysisParameter(parameterSymbol, context);
}
}
private static void AnalysisParameter(IParameterSymbol symbol, SymbolAnalysisContext context)
{
var parentSymbol = context.Symbol;
if (symbol.Type.IsVisibleInternalWithoutFriendAttribute(context.Compilation))
{
var diagnostic = Diagnostic.Create(
Descriptors.AZC0112,
parentSymbol.Locations[0],
$"Parameter {symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)} has a type which",
symbol.Type.ContainingAssembly.Identity.Name);
context.ReportDiagnostic(diagnostic);
}
}
private static void AnalysisField(IFieldSymbol symbol, SymbolAnalysisContext context)
{
var parentSymbol = context.Symbol;
if (symbol.Type.IsVisibleInternalWithoutFriendAttribute(context.Compilation))
{
var diagnostic = Diagnostic.Create(
Descriptors.AZC0112,
parentSymbol.Locations[0],
$"Field {symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)} has a type which",
symbol.Type.ContainingAssembly.Identity.Name);
context.ReportDiagnostic(diagnostic);
}
}
private static void AnalysisProperty(IPropertySymbol symbol, SymbolAnalysisContext context)
{
var parentSymbol = context.Symbol;
if (symbol.Type.IsVisibleInternalWithoutFriendAttribute(context.Compilation))
{
var diagnostic = Diagnostic.Create(
Descriptors.AZC0112,
parentSymbol.Locations[0],
$"Property with type {symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)} has a type which",
symbol.Type.ContainingAssembly.Identity.Name);
context.ReportDiagnostic(diagnostic);
}
}
private static void AnalysisMethod(IMethodSymbol symbol, SymbolAnalysisContext context)
{
var parentSymbol = context.Symbol;
if (symbol.ReturnType.IsVisibleInternalWithoutFriendAttribute(context.Compilation))
{
var diagnostic = Diagnostic.Create(
Descriptors.AZC0112,
parentSymbol.Locations[0],
$"Method {symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)} returns a type which",
symbol.ReturnType.ContainingAssembly.Identity.Name);
context.ReportDiagnostic(diagnostic);
}
}
private static void AnalysisNamedType(INamedTypeSymbol symbol, SymbolAnalysisContext context)
{
var parentSymbol = context.Symbol;
if (symbol.Interfaces.Length > 0)
{
foreach (var interfaceSymbol in symbol.Interfaces)
{
if (interfaceSymbol.IsVisibleInternalWithoutFriendAttribute(context.Compilation))
{
var diagnostic = Diagnostic.Create(
Descriptors.AZC0112,
parentSymbol.Locations[0],
$"Type {parentSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)} implements interface {interfaceSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)} which",
interfaceSymbol.ContainingAssembly.Identity.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
if (symbol.BaseType != null)
{
if (symbol.BaseType.IsVisibleInternalWithoutFriendAttribute(context.Compilation))
{
var diagnostic = Diagnostic.Create(
Descriptors.AZC0112,
parentSymbol.Locations[0],
$"Type {parentSymbol.Name} derives from base type {symbol.BaseType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)} which",
symbol.BaseType.ContainingAssembly.Identity.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
private static void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context)
{
var memberAccess = (MemberAccessExpressionSyntax)context.Node;
if (memberAccess.Expression is IdentifierNameSyntax identifierName && memberAccess.Name is IdentifierNameSyntax memberName)
{
var symbol = context.SemanticModel.GetSymbolInfo(memberName).Symbol;
if (symbol is IPropertySymbol propertySymbol && propertySymbol.IsVisibleInternalWithoutFriendAttribute(context.Compilation))
{
var diagnostic = Diagnostic.Create(
Descriptors.AZC0112,
memberAccess.GetLocation(),
$"Accessed property {propertySymbol.Name} has a type which",
propertySymbol.Type.ContainingAssembly.Identity.Name);
context.ReportDiagnostic(diagnostic);
}
else if(symbol is IMethodSymbol methodSymbol && methodSymbol.IsVisibleInternalWithoutFriendAttribute(context.Compilation))
{
var diagnostic = Diagnostic.Create(
Descriptors.AZC0112,
memberAccess.GetLocation(),
$"Accessed method {methodSymbol.Name} has a return type which",
methodSymbol.ReturnType.ContainingAssembly.Identity.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
}
public static class SymbolExtensions
{
public static bool IsVisibleInternalWithoutFriendAttribute(this ISymbol symbol, Compilation contextCompilation) =>
symbol.DeclaredAccessibility == Accessibility.Internal &&
symbol.ContainingAssembly != null &&
symbol.ContainingAssembly.Identity != contextCompilation.Assembly.Identity &&
!symbol.GetAttributes().Any(ad => ad.AttributeClass.Name == "FriendAttribute");
}
}

Просмотреть файл

@ -0,0 +1,30 @@
namespace TestReferenceWithInternalsVisibleTo
{
internal class InternalClass
{
}
[Friend("TestProject")]
internal class InternalClassWithFriendAttribute
{
}
public class PublicClass
{
public int PublicProperty { get; set; }
internal int InternalProperty { get; set; }
[Friend("TestProject")]
internal int InternalPropertyWithFriendAttribute { get; set; }
public void PublicMethod()
{ }
internal void InternalMethod()
{ }
[Friend("TestProject")]
internal void InternalMethodWithFriendAttribute()
{ }
}
}

Просмотреть файл

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
internal class FriendAttribute : Attribute
{
public string FriendAssembly { get; }
public FriendAttribute(string friendAssembly)
{
FriendAssembly = friendAssembly;
}
}

Просмотреть файл

@ -0,0 +1,15 @@
namespace TestReferenceWithInternalsVisibleTo
{
internal interface IInternalInterface
{
}
[Friend("TestProject")]
internal interface IInternalInterfaceWithFriendAttribute
{
}
public interface IPublicInterface
{
}
}

Просмотреть файл

@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("TestProject")]

Просмотреть файл

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<SignAssembly>False</SignAssembly>
</PropertyGroup>
</Project>