Skip to content

Commit e9c8dc5

Browse files
committed
Additional tests and fixes for qualified using directives
1 parent 857819a commit e9c8dc5

7 files changed

Lines changed: 252 additions & 69 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1135CodeFixProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
6565
private static SyntaxNode GetReplacementNode(SemanticModel semanticModel, UsingDirectiveSyntax node, CancellationToken cancellationToken)
6666
{
6767
SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(node.Name, cancellationToken);
68-
var symbolNameSyntax = SyntaxFactory.ParseName(symbolInfo.Symbol.ToQualifiedString());
68+
var symbolNameSyntax = SyntaxFactory.ParseName(symbolInfo.Symbol.ToQualifiedString(node.Name));
6969

7070
var newName = GetReplacementName(symbolNameSyntax, node.Name);
7171
return node.WithName((NameSyntax)newName);

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp7/ReadabilityRules/SA1135CSharp7UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp7.ReadabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.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 SA1135CSharp7UnitTests : SA1135UnitTests
916
{
17+
[Fact]
18+
[WorkItem(2879, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2879")]
19+
public async Task TestTupleTypeInUsingAliasAsync()
20+
{
21+
var testCode = @"
22+
namespace TestNamespace
23+
{
24+
using Example = System.Collections.Generic.List<(int, int)>;
25+
}
26+
";
27+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
28+
}
29+
30+
[Fact]
31+
[WorkItem(2879, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2879")]
32+
public async Task TestTupleTypeWithNamedElementsInUsingAliasAsync()
33+
{
34+
var testCode = @"
35+
namespace TestNamespace
36+
{
37+
using Example = System.Collections.Generic.List<(int x, int y)>;
38+
}
39+
";
40+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
41+
}
1042
}
1143
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,5 +332,26 @@ namespace TestNamespace
332332
";
333333
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
334334
}
335+
336+
[Fact]
337+
[WorkItem(2879, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2879")]
338+
public async Task TestValueTupleInUsingAliasAsync()
339+
{
340+
var testCode = @"
341+
namespace System
342+
{
343+
using Example = System.Collections.Generic.List<ValueTuple<int, int>>;
344+
}
345+
";
346+
var fixedCode = @"
347+
namespace System
348+
{
349+
using Example = System.Collections.Generic.List<System.ValueTuple<int, int>>;
350+
}
351+
";
352+
353+
var expected = Diagnostic(SA1135UsingDirectivesMustBeQualified.DescriptorType).WithLocation(4, 5).WithArguments("System.Collections.Generic.List<System.ValueTuple<int, int>>");
354+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
355+
}
335356
}
336357
}

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SymbolNameHelpers.cs

Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,43 +17,29 @@ internal static class SymbolNameHelpers
1717
private const string GenericTypeParametersClose = ">";
1818
private const string GenericSeparator = ", ";
1919

20+
private const string TupleTypeOpen = "(";
21+
private const string TupleTypeClose = ")";
22+
private const string TupleElementSeparator = ", ";
23+
2024
/// <summary>
2125
/// Generates the qualified name for the given symbol.
2226
/// </summary>
2327
/// <param name="symbol">The symbol to use.</param>
28+
/// <param name="name">The syntax node which resolves to the symbol.</param>
2429
/// <returns>The generated qualified name.</returns>
25-
public static string ToQualifiedString(this ISymbol symbol)
30+
public static string ToQualifiedString(this ISymbol symbol, NameSyntax name)
2631
{
2732
var builder = ObjectPools.StringBuilderPool.Allocate();
28-
29-
if (symbol is INamedTypeSymbol namedTypeSymbol)
30-
{
31-
if (SpecialTypeHelper.TryGetPredefinedType(namedTypeSymbol.SpecialType, out PredefinedTypeSyntax specialTypeSyntax))
32-
{
33-
return specialTypeSyntax.ToFullString();
34-
}
35-
36-
if (namedTypeSymbol.IsTupleType())
37-
{
38-
namedTypeSymbol = namedTypeSymbol.TupleUnderlyingType();
39-
}
40-
41-
AppendQualifiedSymbolName(builder, namedTypeSymbol);
42-
}
43-
else
44-
{
45-
AppendQualifiedSymbolName(builder, symbol);
46-
}
47-
33+
AppendQualifiedSymbolName(builder, symbol, name);
4834
return ObjectPools.StringBuilderPool.ReturnAndFree(builder);
4935
}
5036

51-
private static bool AppendQualifiedSymbolName(StringBuilder builder, ISymbol symbol)
37+
private static bool AppendQualifiedSymbolName(StringBuilder builder, ISymbol symbol, TypeSyntax type)
5238
{
5339
switch (symbol)
5440
{
5541
case IArrayTypeSymbol arraySymbol:
56-
AppendQualifiedSymbolName(builder, arraySymbol.ElementType);
42+
AppendQualifiedSymbolName(builder, arraySymbol.ElementType, (type as ArrayTypeSyntax)?.ElementType);
5743
builder
5844
.Append("[")
5945
.Append(',', arraySymbol.Rank - 1)
@@ -70,36 +56,79 @@ private static bool AppendQualifiedSymbolName(StringBuilder builder, ISymbol sym
7056
return true;
7157

7258
case INamedTypeSymbol namedTypeSymbol:
73-
if (AppendQualifiedSymbolName(builder, symbol.ContainingSymbol))
59+
if (SpecialTypeHelper.TryGetPredefinedType(namedTypeSymbol.SpecialType, out var specialTypeSyntax))
7460
{
75-
builder.Append(".");
61+
builder.Append(specialTypeSyntax.ToFullString());
62+
return true;
7663
}
77-
78-
builder.Append(symbol.Name);
79-
if (namedTypeSymbol.IsGenericType && !namedTypeSymbol.TypeArguments.IsEmpty)
64+
else if (namedTypeSymbol.IsTupleType())
8065
{
81-
builder.Append(GenericTypeParametersOpen);
82-
83-
foreach (var typeArgument in namedTypeSymbol.TypeArguments)
66+
if (TupleTypeSyntaxWrapper.IsInstance(type))
8467
{
85-
if (typeArgument is INamedTypeSymbol namedTypeArgument && typeArgument.IsTupleType())
68+
var tupleType = (TupleTypeSyntaxWrapper)type;
69+
70+
builder.Append(TupleTypeOpen);
71+
var elements = namedTypeSymbol.TupleElements();
72+
for (int i = 0; i < elements.Length; i++)
8673
{
87-
builder.Append(namedTypeArgument.TupleUnderlyingType().ToQualifiedString());
74+
var field = elements[i];
75+
var fieldType = tupleType.Elements.Count > i ? tupleType.Elements[i] : default;
76+
77+
if (i > 0)
78+
{
79+
builder.Append(TupleElementSeparator);
80+
}
81+
82+
AppendQualifiedSymbolName(builder, field.Type, fieldType.Type);
83+
if (field != field.CorrespondingTupleField())
84+
{
85+
builder.Append(" ").Append(field.Name);
86+
}
8887
}
89-
else
88+
89+
builder.Append(TupleTypeClose);
90+
return true;
91+
}
92+
else
93+
{
94+
return AppendQualifiedSymbolName(builder, namedTypeSymbol.TupleUnderlyingType(), type);
95+
}
96+
}
97+
else
98+
{
99+
if (AppendQualifiedSymbolName(builder, symbol.ContainingSymbol, (type as QualifiedNameSyntax)?.Left))
100+
{
101+
builder.Append(".");
102+
}
103+
104+
builder.Append(symbol.Name);
105+
if (namedTypeSymbol.IsGenericType && !namedTypeSymbol.TypeArguments.IsEmpty)
106+
{
107+
builder.Append(GenericTypeParametersOpen);
108+
var arguments = namedTypeSymbol.TypeArguments;
109+
var argumentTypes = type is QualifiedNameSyntax qualifiedName
110+
? (qualifiedName.Right as GenericNameSyntax)?.TypeArgumentList
111+
: (type as GenericNameSyntax)?.TypeArgumentList;
112+
113+
for (int i = 0; i < arguments.Length; i++)
90114
{
91-
builder.Append(typeArgument.ToQualifiedString());
115+
var argument = arguments[i];
116+
var argumentType = argumentTypes.Arguments.Count > i ? argumentTypes.Arguments[i] : null;
117+
118+
if (i > 0)
119+
{
120+
builder.Append(GenericSeparator);
121+
}
122+
123+
AppendQualifiedSymbolName(builder, argument, argumentType);
92124
}
93125

94-
builder.Append(GenericSeparator);
126+
builder.Append(GenericTypeParametersClose);
95127
}
96128

97-
builder.Remove(builder.Length - GenericSeparator.Length, GenericSeparator.Length);
98-
builder.Append(GenericTypeParametersClose);
129+
return true;
99130
}
100131

101-
return true;
102-
103132
default:
104133
builder.Append(symbol.Name);
105134
return true;

StyleCop.Analyzers/StyleCop.Analyzers/Lightup/CSharp7.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ See [dotnet/roslyn@c2af711](https://github.com/dotnet/roslyn/commit/c2af71127234
6161
* [ ] `Microsoft.CodeAnalysis.IArrayTypeSymbol.Sizes.get -> System.Collections.Immutable.ImmutableArray<int>`
6262
* [ ] `Microsoft.CodeAnalysis.IDiscardSymbol`
6363
* [ ] `Microsoft.CodeAnalysis.IDiscardSymbol.Type.get -> Microsoft.CodeAnalysis.ITypeSymbol`
64-
* [ ] `Microsoft.CodeAnalysis.IFieldSymbol.CorrespondingTupleField.get -> Microsoft.CodeAnalysis.IFieldSymbol`
64+
* [x] `Microsoft.CodeAnalysis.IFieldSymbol.CorrespondingTupleField.get -> Microsoft.CodeAnalysis.IFieldSymbol`
6565
* [ ] `Microsoft.CodeAnalysis.ILocalSymbol.IsRef.get -> bool`
6666
* [ ] `Microsoft.CodeAnalysis.IMethodSymbol.RefCustomModifiers.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.CustomModifier>`
6767
* [ ] `Microsoft.CodeAnalysis.IMethodSymbol.ReturnsByRef.get -> bool`
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Lightup
5+
{
6+
using System;
7+
using Microsoft.CodeAnalysis;
8+
9+
internal static class IFieldSymbolExtensions
10+
{
11+
private static readonly Func<IFieldSymbol, IFieldSymbol> CorrespondingTupleFieldAccessor;
12+
13+
static IFieldSymbolExtensions()
14+
{
15+
CorrespondingTupleFieldAccessor = LightupHelpers.CreateSyntaxPropertyAccessor<IFieldSymbol, IFieldSymbol>(typeof(IFieldSymbol), nameof(CorrespondingTupleField));
16+
}
17+
18+
public static IFieldSymbol CorrespondingTupleField(this IFieldSymbol symbol)
19+
{
20+
return CorrespondingTupleFieldAccessor(symbol);
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)