Skip to content

Commit

Permalink
feat(analyzer): add rules regarding model name suffixes (#6916)
Browse files Browse the repository at this point in the history
- add four rules checking the suffixes of model names
- upgrade Roslyn to `4.4.0.0` since new rules require some API
- fix and suppress warnings due to upgrade of Roslyn
- temporarily suppress AD0001
- minor refactoring of existing codes
- add test cases for the new rules

resolve #6905

---------

Co-authored-by: Mingzhe Huang (from Dev Box) <[email protected]>
Co-authored-by: Christopher Scott <[email protected]>
  • Loading branch information
3 people authored Oct 26, 2023
1 parent da308e1 commit d52c068
Show file tree
Hide file tree
Showing 17 changed files with 947 additions and 35 deletions.
23 changes: 23 additions & 0 deletions src/dotnet/Azure.ClientSdk.Analyzers/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# The warnings below are found after upgrading to Roslyn 4.7.0
# It should be able to fix them all. Suppressing them is just a temporary work-around.

[*.cs]

# CS0618: 'IOperation.Children' is obsolete: 'This API has performance penalties, please use ChildOperations instead.
dotnet_diagnostic.CS0618.severity = none

# RS1031: The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces
dotnet_diagnostic.RS1031.severity = none

# RS1032: The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period
dotnet_diagnostic.RS1032.severity = none

# RS2008: Enable analyzer release tracking for the analyzer project containing rule 'XXX'
dotnet_diagnostic.RS2008.severity = none

# RS1024: Use 'SymbolEqualityComparer' when comparing symbols
dotnet_diagnostic.RS1024.severity = none

# RS1037: Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field 'AZC0011' as it is used to report a compilation end diagnostic
dotnet_diagnostic.RS1037.severity = none

Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.3" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />

<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Reflection.Metadata" Version="1.8.0" />
<PackageReference Include="System.Reflection.Metadata" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="3.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.4.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('net'))">
<PackageReference Include="System.Interactive.Async" Version="4.0.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System.Threading.Tasks;
using Azure.ClientSdk.Analyzers.ModelName;
using Xunit;

using VerifyCS = Azure.ClientSdk.Analyzers.Tests.AzureAnalyzerVerifier<
Azure.ClientSdk.Analyzers.ModelName.GeneralSuffixAnalyzer>;

namespace Azure.ClientSdk.Analyzers.Tests.ModelName
{
public class AZC0030Tests
{
private const string diagnosticId = "AZC0030";

[Fact]
public async Task GoodSuffix()
{
var test = @"using System.Text.Json;
namespace Azure.ResourceManager.Models;
public class MonitorContent
{
}";
await VerifyCS.VerifyAnalyzerAsync(test);
}

[Fact]
public async Task ParametersSuffix()
{
var test = @"using System.Text.Json;
namespace Azure.ResourceManager
{
public class ResponseParameters
{
public static ResponseParameters DeserializeResponseParameters(JsonElement element)
{
return null;
}
}
}";
var expected = VerifyCS.Diagnostic(diagnosticId).WithSpan(4, 18, 4, 36).WithArguments("ResponseParameters", "Parameters", "'ResponseContent' or 'ResponsePatch'");
await VerifyCS.VerifyAnalyzerAsync(test, expected);
}

[Fact]
public async Task RequestSuffix()
{
var test = @"using System.Text.Json;
namespace Azure.ResourceManager.Models
{
public class DiskOption
{
public static DiskOption DeserializeDiskOption(JsonElement element)
{
return null;
}
}
}";
var expected = VerifyCS.Diagnostic(diagnosticId).WithSpan(4, 18, 4, 28).WithArguments("DiskOption", "Option", "'DiskConfig'");
await VerifyCS.VerifyAnalyzerAsync(test, expected);
}

[Fact]
public async Task OptionSuffixWithNestedNameSpace()
{
var test = @"using System.Text.Json;
namespace Azure.ResourceManager.Models
{
namespace SubTest
{
public class DiskOption
{
}
}
}";
var expected = VerifyCS.Diagnostic(diagnosticId).WithSpan(6, 22, 6, 32).WithArguments("DiskOption", "Option", "'DiskConfig'");
await VerifyCS.VerifyAnalyzerAsync(test, expected);
}

[Fact]
public async Task ResponsesSuffix()
{
var test = @"using System.Text.Json;
namespace Azure.ResourceManager.Models
{
namespace SubTest
{
public class CreationResponses
{
public static CreationResponses DeserializeCreationResponses(JsonElement element)
{
return null;
}
}
}
}";
var expected = VerifyCS.Diagnostic(diagnosticId).WithSpan(6, 22, 6, 39).WithArguments("CreationResponses", "Responses", "'CreationResults'");
await VerifyCS.VerifyAnalyzerAsync(test, expected);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Threading.Tasks;
using Azure.ClientSdk.Analyzers.ModelName;
using Xunit;

using VerifyCS = Azure.ClientSdk.Analyzers.Tests.AzureAnalyzerVerifier<
Azure.ClientSdk.Analyzers.ModelName.DefinitionSuffixAnalyzer>;

namespace Azure.ClientSdk.Analyzers.Tests.ModelName
{
public class AZC0031Tests
{
private const string diagnosticId = "AZC0031";

[Fact]
public async Task ModelWithDefinitionSuffix()
{
var test = @"using System.Text.Json;
namespace Azure.ResourceManager.Network.Models
{
public partial class AadAuthenticationDefinition
{
}
}";
var expected = VerifyCS.Diagnostic(diagnosticId).WithSpan(4, 26, 4, 53).WithArguments("AadAuthenticationDefinition", "Definition");
await VerifyCS.VerifyAnalyzerAsync(test, expected);
}

[Fact]
public async Task ArmResourceIsNotChecked()
{
var test = @"using System.Text.Json;
using Azure.ResourceManager;
namespace Azure.ResourceManager
{
public class ArmResource {
}
}
namespace Azure.ResourceManager.Network.Models
{
public partial class AadAuthenticationDefinition: ArmResource
{
public static AadAuthenticationDefinition DeserializeAadAuthenticationDefinition(JsonElement element)
{
return null;
}
}
}";
await VerifyCS.VerifyAnalyzerAsync(test);
}

[Fact]
public async Task NotCheckIfRemovingSuffixIsAnotherType()
{
var test = @"using System.Text.Json;
using Azure.ResourceManager;
namespace Azure.ResourceManager.Network.Models
{
public class AadAuthentication {
}
public partial class AadAuthenticationDefinition
{
public static AadAuthenticationDefinition DeserializeAadAuthenticationDefinition(JsonElement element)
{
return null;
}
}
}";
await VerifyCS.VerifyAnalyzerAsync(test);
}

}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System.Threading.Tasks;
using Xunit;

using VerifyCS = Azure.ClientSdk.Analyzers.Tests.AzureAnalyzerVerifier<
Azure.ClientSdk.Analyzers.ModelName.DataSuffixAnalyzer>;
using Azure.ClientSdk.Analyzers.ModelName;

namespace Azure.ClientSdk.Analyzers.Tests.ModelName
{
public class AZC0032Tests
{
private const string diagnosticId = "AZC0032";

[Fact]
public async Task ModelClassWithDataSuffix()
{
var test = @"using System.Text.Json;
namespace Azure.ResourceManager.Network.Models
{
public partial class AadAuthenticationData
{
public static AadAuthenticationData DeserializeAadAuthenticationData(JsonElement element)
{
return null;
}
}
}";
var expected = VerifyCS.Diagnostic(diagnosticId).WithSpan(4, 26, 4, 47).WithArguments("AadAuthenticationData", "Data");
await VerifyCS.VerifyAnalyzerAsync(test, expected);
}

[Fact]
public async Task ResourceDataClassesAreNotChecked()
{
var test = @"using System.Text.Json;
using Azure.ResourceManager.Models;
namespace Azure.ResourceManager.Models
{
public class ResourceData {
}
}
namespace Azure.ResourceManager.Models.Network
{
public partial class AadAuthenticationData: ResourceData
{
}
}";
await VerifyCS.VerifyAnalyzerAsync(test);
}

[Fact]
public async Task TrackedResourceDataClassesAreNotChecked()
{
var test = @"using System.Text.Json;
using Azure.ResourceManager.Models;
namespace Azure.ResourceManager.Models
{
public class TrackedResourceData {
}
}
namespace Azure.ResourceManager.Network.Models
{
public partial class AadAuthenticationData: TrackedResourceData
{
}
}";
await VerifyCS.VerifyAnalyzerAsync(test);
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Threading.Tasks;
using Azure.ClientSdk.Analyzers.ModelName;
using Microsoft.CodeAnalysis.Testing;
using Xunit;

using VerifyCS = Azure.ClientSdk.Analyzers.Tests.AzureAnalyzerVerifier<
Azure.ClientSdk.Analyzers.ModelName.OperationSuffixAnalyzer>;

namespace Azure.ClientSdk.Analyzers.Tests.ModelName
{
public class AZC0033Tests
{
private const string diagnosticId = "AZC0033";

[Fact]
public async Task OperationClassIsNotChecked()
{
var test = @"using System.Text.Json;
using Azure;
using Azure.ResourceManager;
namespace Azure
{
public class Operation
{
}
public class Operation<T>
{
}
}
namespace Azure.ResourceManager
{
public class ArmOperation : Operation
{
}
public class ArmOperation<T> : Operation<T>
{
}
}
namespace Azure.ResourceManager.Network.Models
{
public class DnsOperation : Operation
{
}
public class DnsArmOperation : ArmOperation
{
}
public class DnsOperation<T> : Operation<T>
{
}
public class DnsArmOperation<T> : ArmOperation<T>
{
}
}";
await VerifyCS.VerifyAnalyzerAsync(test);
}

[Fact]
public async Task OperationSuffix()
{
var test = @"using System.Text.Json;
namespace Azure.ResourceManager.Network
{
public class DnsOperation
{
public static DnsOperation DeserializeDnsOperation(JsonElement element)
{
return null;
}
}
public class DnsArmOperation<T>
{
public static DnsArmOperation<T> DeserializeDnsArmOperation(JsonElement element)
{
return null;
}
}
}";
DiagnosticResult[] expected = {
VerifyCS.Diagnostic(diagnosticId).WithSpan(4, 18, 4, 30).WithArguments("DnsOperation", "Operation", "DnsData", "DnsInfo"),
VerifyCS.Diagnostic(diagnosticId).WithSpan(11, 18, 11, 33).WithArguments("DnsArmOperation", "Operation", "DnsArmData", "DnsArmInfo")
};
await VerifyCS.VerifyAnalyzerAsync(test, expected);
}
}
}

Loading

0 comments on commit d52c068

Please sign in to comment.