Skip to content

Commit b4d0192

Browse files
authored
Merge pull request #3157 from dymanoid/sa1135-types-handling
Fix incorrect handling of types in SA1135
2 parents 9c9bc1c + 76d6c67 commit b4d0192

File tree

6 files changed

+384
-68
lines changed

6 files changed

+384
-68
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Test.CSharp7.HelperTests
5+
{
6+
using System.Threading.Tasks;
7+
using StyleCop.Analyzers.Test.HelperTests;
8+
using Xunit;
9+
10+
/// <summary>
11+
/// Unit tests for the <see cref="StyleCop.Analyzers.Helpers.SymbolNameHelpers"/> class in the context of C# 7.x.
12+
/// </summary>
13+
public class SymbolNameHelpersCSharp7Tests : SymbolNameHelpersTests
14+
{
15+
/// <summary>
16+
/// Verify the workings of <see cref="StyleCop.Analyzers.Helpers.SymbolNameHelpers.ToQualifiedString(Microsoft.CodeAnalysis.ISymbol, Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax)"/>
17+
/// for standard use cases.
18+
/// </summary>
19+
/// <param name="inputString">A string representation of a type or namespace to process.</param>
20+
/// <param name="isNamespace"><see langword="true"/> if <paramref name="inputString"/> is a namespace;
21+
/// <see langword="false"/> if <paramref name="inputString"/> is a type to be used as an alias target.</param>
22+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
23+
[Theory]
24+
[InlineData("System.ValueTuple<int, object[]>")]
25+
[InlineData("System.ValueTuple<int, (int, object)>")]
26+
[InlineData("System.ValueTuple<int, (int, object)?>")]
27+
[InlineData("System.ValueTuple<int, (int, object[])>")]
28+
[InlineData("System.Collections.Generic.KeyValuePair<int, (int, object)>")]
29+
[InlineData("System.Collections.Generic.KeyValuePair<int, (int, object[])>")]
30+
[InlineData("System.Nullable<(int, object)>")]
31+
[WorkItem(3149, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3149")]
32+
public Task VerifyToQualifiedStringTuplesAsync(string inputString, bool isNamespace = false)
33+
{
34+
return this.PerformTestAsync(inputString, isNamespace);
35+
}
36+
}
37+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Test.CSharp8.HelperTests
5+
{
6+
using System.Threading.Tasks;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
9+
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
using StyleCop.Analyzers.Test.CSharp7.HelperTests;
11+
using Xunit;
12+
13+
/// <summary>
14+
/// Unit tests for the <see cref="StyleCop.Analyzers.Helpers.SymbolNameHelpers"/> class in the context of C# 8.0.
15+
/// </summary>
16+
public class SymbolNameHelpersCSharp8Tests : SymbolNameHelpersCSharp7Tests
17+
{
18+
/// <summary>
19+
/// Verify the workings of <see cref="StyleCop.Analyzers.Helpers.SymbolNameHelpers.ToQualifiedString(ISymbol, NameSyntax)"/>
20+
/// for nullable reference types.
21+
/// </summary>
22+
/// <param name="inputString">A string representation of a type or namespace to process.</param>
23+
/// <param name="isNamespace"><see langword="true"/> if <paramref name="inputString"/> is a namespace;
24+
/// <see langword="false"/> if <paramref name="inputString"/> is a type to be used as an alias target.</param>
25+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
26+
[Theory]
27+
[InlineData("System.ValueTuple<System.Int32, System.Object?>")]
28+
[InlineData("System.ValueTuple<int, object?>")]
29+
[InlineData("System.ValueTuple<int, object?[]>")]
30+
[InlineData("System.ValueTuple<int, object[]?>")]
31+
[InlineData("System.ValueTuple<int, object?[]?>")]
32+
[InlineData("System.ValueTuple<int?[]?, object>")]
33+
[InlineData("System.ValueTuple<int, (int, object?)>")]
34+
[InlineData("System.ValueTuple<int, (int, object?)?>")]
35+
[InlineData("System.ValueTuple<int, (int, object?[])>")]
36+
[InlineData("System.ValueTuple<int, (int, object[]?)>")]
37+
[InlineData("System.ValueTuple<int, (int, object?[]?)>")]
38+
[InlineData("System.Collections.Generic.KeyValuePair<int, object?>")]
39+
[InlineData("System.Collections.Generic.KeyValuePair<int, object?[]>")]
40+
[InlineData("System.Collections.Generic.KeyValuePair<int, object[]?>")]
41+
[InlineData("System.Collections.Generic.KeyValuePair<int, object?[]?>")]
42+
[InlineData("System.Collections.Generic.KeyValuePair<int, (int, object?)>")]
43+
[InlineData("System.Collections.Generic.KeyValuePair<int, (int, object?[])>")]
44+
[InlineData("System.Collections.Generic.KeyValuePair<int, (int, object[]?)>")]
45+
[InlineData("System.Collections.Generic.KeyValuePair<int, (int, object?[]?)>")]
46+
[InlineData("System.Nullable<(int, object?)>")]
47+
[WorkItem(3149, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3149")]
48+
public Task VerifyToQualifiedStringNullableReferenceTypesAsync(string inputString, bool isNamespace = false)
49+
{
50+
return this.PerformTestAsync(inputString, isNamespace);
51+
}
52+
53+
/// <summary>
54+
/// Provides a <see cref="CSharpCompilationOptions"/> instance with enabled nullable reference types
55+
/// to be used for the test project in the workspace.
56+
/// </summary>
57+
/// <returns>An instance of <see cref="CSharpCompilationOptions"/> describing the project compilation options.</returns>
58+
protected override CSharpCompilationOptions GetCompilationOptions()
59+
{
60+
return new CSharpCompilationOptions(
61+
OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Enable);
62+
}
63+
}
64+
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/ReadabilityRules/SA1135CSharp8UnitTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,28 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp8.ReadabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp7.ReadabilityRules;
10+
using Xunit;
11+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
12+
StyleCop.Analyzers.ReadabilityRules.SA1135UsingDirectivesMustBeQualified,
13+
StyleCop.Analyzers.ReadabilityRules.SA1135CodeFixProvider>;
714

815
public class SA1135CSharp8UnitTests : SA1135CSharp7UnitTests
916
{
17+
[Fact]
18+
[WorkItem(3149, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3149")]
19+
public async Task TestAliasTypeGenericNullableReferenceTypeAsync()
20+
{
21+
var testCode = @"
22+
namespace TestNamespace
23+
{
24+
using KeyValue = System.Collections.Generic.KeyValuePair<string, object?>;
25+
}
26+
";
27+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
28+
}
1029
}
1130
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Test.HelperTests
5+
{
6+
using System.Linq;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.CSharp;
11+
using Microsoft.CodeAnalysis.CSharp.Syntax;
12+
using Microsoft.CodeAnalysis.Text;
13+
using StyleCop.Analyzers.Helpers;
14+
using StyleCop.Analyzers.Test.Verifiers;
15+
using Xunit;
16+
17+
/// <summary>
18+
/// Unit tests for the <see cref="SymbolNameHelpers"/> class.
19+
/// </summary>
20+
public class SymbolNameHelpersTests : IAsyncLifetime
21+
{
22+
private const string TestProjectName = "TestProject";
23+
private const string TestFilename = "Test.cs";
24+
25+
private Solution testSolution;
26+
27+
public async Task InitializeAsync()
28+
{
29+
var compilationOptions = this.GetCompilationOptions();
30+
this.testSolution = await CreateTestSolutionAsync(compilationOptions).ConfigureAwait(false);
31+
}
32+
33+
public Task DisposeAsync()
34+
{
35+
return Task.FromResult(true);
36+
}
37+
38+
/// <summary>
39+
/// Verify the workings of <see cref="SymbolNameHelpers.ToQualifiedString(ISymbol, NameSyntax)"/>
40+
/// for standard use cases.
41+
/// </summary>
42+
/// <param name="inputString">A string representation of a type or namespace to process.</param>
43+
/// <param name="isNamespace"><see langword="true"/> if <paramref name="inputString"/> is a namespace;
44+
/// <see langword="false"/> if <paramref name="inputString"/> is a type to be used as an alias target.</param>
45+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
46+
[Theory]
47+
[InlineData("System.ValueTuple<int, object>")]
48+
[InlineData("System.ValueTuple<System.Int32, System.Object>")]
49+
[InlineData("System.ValueTuple<System.Int32?, System.Object>")]
50+
[InlineData("System.ValueTuple<int, object[]>")]
51+
[InlineData("System.ValueTuple<int?, object>")]
52+
[InlineData("System.ValueTuple<int[], object>")]
53+
[InlineData("System.ValueTuple<int?[], object>")]
54+
[InlineData("System.Collections.Generic.KeyValuePair<int, object>")]
55+
[InlineData("System.Collections.Generic.KeyValuePair<int?, object>")]
56+
[InlineData("System.Collections.Generic.KeyValuePair<int, object[]>")]
57+
[InlineData("System.Nullable<int>")]
58+
[InlineData("System", true)]
59+
[InlineData("System.Collections.Generic", true)]
60+
[WorkItem(3149, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3149")]
61+
public Task VerifyToQualifiedStringAsync(string inputString, bool isNamespace = false)
62+
{
63+
return this.PerformTestAsync(inputString, isNamespace);
64+
}
65+
66+
/// <summary>
67+
/// Performs the actual testing work.
68+
/// </summary>
69+
/// <param name="inputString">A string representation of a type or namespace to process.</param>
70+
/// <param name="isNamespace"><see langword="true"/> if <paramref name="inputString"/> is a namespace;
71+
/// <see langword="false"/> if <paramref name="inputString"/> is a type to be used as an alias target.</param>
72+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
73+
protected async Task PerformTestAsync(string inputString, bool isNamespace)
74+
{
75+
var fileContent = isNamespace ? "using " + inputString : "using AliasType = " + inputString;
76+
fileContent += ";";
77+
78+
var document = GetDocument(this.testSolution, fileContent);
79+
var syntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false);
80+
var semanticModel = await document.GetSemanticModelAsync().ConfigureAwait(false);
81+
82+
var usingDirectiveSyntax = syntaxRoot.DescendantNodes().OfType<UsingDirectiveSyntax>().First();
83+
var typeSymbol = semanticModel.GetSymbolInfo(usingDirectiveSyntax.Name).Symbol;
84+
85+
var resultString = typeSymbol.ToQualifiedString(usingDirectiveSyntax.Name);
86+
Assert.Equal(inputString, resultString);
87+
}
88+
89+
/// <summary>
90+
/// When overridden in derived classes, provides a <see cref="CSharpCompilationOptions"/> instance
91+
/// to be used for the test project in the workspace.
92+
/// </summary>
93+
/// <returns>An instance of <see cref="CSharpCompilationOptions"/> describing the project compilation options.</returns>
94+
protected virtual CSharpCompilationOptions GetCompilationOptions()
95+
{
96+
return new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
97+
}
98+
99+
private static Document GetDocument(Solution solution, string sourceCode)
100+
{
101+
var projectId = solution.ProjectIds[0];
102+
var documentId = DocumentId.CreateNewId(projectId);
103+
solution = solution.AddDocument(documentId, TestFilename, SourceText.From(sourceCode));
104+
return solution.GetDocument(documentId);
105+
}
106+
107+
private static async Task<Solution> CreateTestSolutionAsync(CSharpCompilationOptions compilationOptions)
108+
{
109+
var workspace = GenericAnalyzerTest.CreateWorkspace();
110+
var projectId = ProjectId.CreateNewId();
111+
112+
var references = await GenericAnalyzerTest.ReferenceAssemblies
113+
.ResolveAsync(LanguageNames.CSharp, CancellationToken.None).ConfigureAwait(false);
114+
115+
var solution = workspace.CurrentSolution
116+
.AddProject(projectId, TestProjectName, TestProjectName, LanguageNames.CSharp)
117+
.WithProjectCompilationOptions(projectId, compilationOptions)
118+
.AddMetadataReferences(projectId, references);
119+
120+
return solution;
121+
}
122+
}
123+
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1135UnitTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,5 +415,31 @@ namespace System
415415
var expected = Diagnostic(SA1135UsingDirectivesMustBeQualified.DescriptorType).WithLocation(4, 5).WithArguments("System.Collections.Generic.List<System.ValueTuple<int, int>>");
416416
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
417417
}
418+
419+
[Fact]
420+
[WorkItem(3149, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3149")]
421+
public async Task TestAliasTypeClrTypeAsync()
422+
{
423+
var testCode = @"
424+
namespace TestNamespace
425+
{
426+
using Example = System.Collections.Generic.List<System.Object>;
427+
}
428+
";
429+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
430+
}
431+
432+
[Fact]
433+
[WorkItem(3149, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3149")]
434+
public async Task TestAliasTypeGenericNullableAsync()
435+
{
436+
var testCode = @"
437+
namespace TestNamespace
438+
{
439+
using Example = System.Nullable<int>;
440+
}
441+
";
442+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
443+
}
418444
}
419445
}

0 commit comments

Comments
 (0)