Skip to content

Commit

Permalink
Additional tests and fixes for qualified using directives
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Feb 14, 2019
1 parent 857819a commit e9c8dc5
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
private static SyntaxNode GetReplacementNode(SemanticModel semanticModel, UsingDirectiveSyntax node, CancellationToken cancellationToken)
{
SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(node.Name, cancellationToken);
var symbolNameSyntax = SyntaxFactory.ParseName(symbolInfo.Symbol.ToQualifiedString());
var symbolNameSyntax = SyntaxFactory.ParseName(symbolInfo.Symbol.ToQualifiedString(node.Name));

var newName = GetReplacementName(symbolNameSyntax, node.Name);
return node.WithName((NameSyntax)newName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,41 @@

namespace StyleCop.Analyzers.Test.CSharp7.ReadabilityRules
{
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.ReadabilityRules;
using Xunit;
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
StyleCop.Analyzers.ReadabilityRules.SA1135UsingDirectivesMustBeQualified,
StyleCop.Analyzers.ReadabilityRules.SA1135CodeFixProvider>;

public class SA1135CSharp7UnitTests : SA1135UnitTests
{
[Fact]
[WorkItem(2879, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2879")]
public async Task TestTupleTypeInUsingAliasAsync()
{
var testCode = @"
namespace TestNamespace
{
using Example = System.Collections.Generic.List<(int, int)>;
}
";
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
[WorkItem(2879, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2879")]
public async Task TestTupleTypeWithNamedElementsInUsingAliasAsync()
{
var testCode = @"
namespace TestNamespace
{
using Example = System.Collections.Generic.List<(int x, int y)>;
}
";
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -332,5 +332,26 @@ namespace TestNamespace
";
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
[WorkItem(2879, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2879")]
public async Task TestValueTupleInUsingAliasAsync()
{
var testCode = @"
namespace System
{
using Example = System.Collections.Generic.List<ValueTuple<int, int>>;
}
";
var fixedCode = @"
namespace System
{
using Example = System.Collections.Generic.List<System.ValueTuple<int, int>>;
}
";

var expected = Diagnostic(SA1135UsingDirectivesMustBeQualified.DescriptorType).WithLocation(4, 5).WithArguments("System.Collections.Generic.List<System.ValueTuple<int, int>>");
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
}
}
}
109 changes: 69 additions & 40 deletions StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SymbolNameHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,29 @@ internal static class SymbolNameHelpers
private const string GenericTypeParametersClose = ">";
private const string GenericSeparator = ", ";

private const string TupleTypeOpen = "(";
private const string TupleTypeClose = ")";
private const string TupleElementSeparator = ", ";

/// <summary>
/// Generates the qualified name for the given symbol.
/// </summary>
/// <param name="symbol">The symbol to use.</param>
/// <param name="name">The syntax node which resolves to the symbol.</param>
/// <returns>The generated qualified name.</returns>
public static string ToQualifiedString(this ISymbol symbol)
public static string ToQualifiedString(this ISymbol symbol, NameSyntax name)
{
var builder = ObjectPools.StringBuilderPool.Allocate();

if (symbol is INamedTypeSymbol namedTypeSymbol)
{
if (SpecialTypeHelper.TryGetPredefinedType(namedTypeSymbol.SpecialType, out PredefinedTypeSyntax specialTypeSyntax))
{
return specialTypeSyntax.ToFullString();
}

if (namedTypeSymbol.IsTupleType())
{
namedTypeSymbol = namedTypeSymbol.TupleUnderlyingType();
}

AppendQualifiedSymbolName(builder, namedTypeSymbol);
}
else
{
AppendQualifiedSymbolName(builder, symbol);
}

AppendQualifiedSymbolName(builder, symbol, name);
return ObjectPools.StringBuilderPool.ReturnAndFree(builder);
}

private static bool AppendQualifiedSymbolName(StringBuilder builder, ISymbol symbol)
private static bool AppendQualifiedSymbolName(StringBuilder builder, ISymbol symbol, TypeSyntax type)
{
switch (symbol)
{
case IArrayTypeSymbol arraySymbol:
AppendQualifiedSymbolName(builder, arraySymbol.ElementType);
AppendQualifiedSymbolName(builder, arraySymbol.ElementType, (type as ArrayTypeSyntax)?.ElementType);
builder
.Append("[")
.Append(',', arraySymbol.Rank - 1)
Expand All @@ -70,36 +56,79 @@ private static bool AppendQualifiedSymbolName(StringBuilder builder, ISymbol sym
return true;

case INamedTypeSymbol namedTypeSymbol:
if (AppendQualifiedSymbolName(builder, symbol.ContainingSymbol))
if (SpecialTypeHelper.TryGetPredefinedType(namedTypeSymbol.SpecialType, out var specialTypeSyntax))
{
builder.Append(".");
builder.Append(specialTypeSyntax.ToFullString());
return true;
}

builder.Append(symbol.Name);
if (namedTypeSymbol.IsGenericType && !namedTypeSymbol.TypeArguments.IsEmpty)
else if (namedTypeSymbol.IsTupleType())
{
builder.Append(GenericTypeParametersOpen);

foreach (var typeArgument in namedTypeSymbol.TypeArguments)
if (TupleTypeSyntaxWrapper.IsInstance(type))
{
if (typeArgument is INamedTypeSymbol namedTypeArgument && typeArgument.IsTupleType())
var tupleType = (TupleTypeSyntaxWrapper)type;

builder.Append(TupleTypeOpen);
var elements = namedTypeSymbol.TupleElements();
for (int i = 0; i < elements.Length; i++)
{
builder.Append(namedTypeArgument.TupleUnderlyingType().ToQualifiedString());
var field = elements[i];
var fieldType = tupleType.Elements.Count > i ? tupleType.Elements[i] : default;

if (i > 0)
{
builder.Append(TupleElementSeparator);
}

AppendQualifiedSymbolName(builder, field.Type, fieldType.Type);
if (field != field.CorrespondingTupleField())
{
builder.Append(" ").Append(field.Name);
}
}
else

builder.Append(TupleTypeClose);
return true;
}
else
{
return AppendQualifiedSymbolName(builder, namedTypeSymbol.TupleUnderlyingType(), type);
}
}
else
{
if (AppendQualifiedSymbolName(builder, symbol.ContainingSymbol, (type as QualifiedNameSyntax)?.Left))
{
builder.Append(".");
}

builder.Append(symbol.Name);
if (namedTypeSymbol.IsGenericType && !namedTypeSymbol.TypeArguments.IsEmpty)
{
builder.Append(GenericTypeParametersOpen);
var arguments = namedTypeSymbol.TypeArguments;
var argumentTypes = type is QualifiedNameSyntax qualifiedName
? (qualifiedName.Right as GenericNameSyntax)?.TypeArgumentList
: (type as GenericNameSyntax)?.TypeArgumentList;

for (int i = 0; i < arguments.Length; i++)
{
builder.Append(typeArgument.ToQualifiedString());
var argument = arguments[i];
var argumentType = argumentTypes.Arguments.Count > i ? argumentTypes.Arguments[i] : null;

if (i > 0)
{
builder.Append(GenericSeparator);
}

AppendQualifiedSymbolName(builder, argument, argumentType);
}

builder.Append(GenericSeparator);
builder.Append(GenericTypeParametersClose);
}

builder.Remove(builder.Length - GenericSeparator.Length, GenericSeparator.Length);
builder.Append(GenericTypeParametersClose);
return true;
}

return true;

default:
builder.Append(symbol.Name);
return true;
Expand Down
2 changes: 1 addition & 1 deletion StyleCop.Analyzers/StyleCop.Analyzers/Lightup/CSharp7.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ See [dotnet/roslyn@c2af711](https://github.com/dotnet/roslyn/commit/c2af71127234
* [ ] `Microsoft.CodeAnalysis.IArrayTypeSymbol.Sizes.get -> System.Collections.Immutable.ImmutableArray<int>`
* [ ] `Microsoft.CodeAnalysis.IDiscardSymbol`
* [ ] `Microsoft.CodeAnalysis.IDiscardSymbol.Type.get -> Microsoft.CodeAnalysis.ITypeSymbol`
* [ ] `Microsoft.CodeAnalysis.IFieldSymbol.CorrespondingTupleField.get -> Microsoft.CodeAnalysis.IFieldSymbol`
* [x] `Microsoft.CodeAnalysis.IFieldSymbol.CorrespondingTupleField.get -> Microsoft.CodeAnalysis.IFieldSymbol`
* [ ] `Microsoft.CodeAnalysis.ILocalSymbol.IsRef.get -> bool`
* [ ] `Microsoft.CodeAnalysis.IMethodSymbol.RefCustomModifiers.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.CustomModifier>`
* [ ] `Microsoft.CodeAnalysis.IMethodSymbol.ReturnsByRef.get -> bool`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

namespace StyleCop.Analyzers.Lightup
{
using System;
using Microsoft.CodeAnalysis;

internal static class IFieldSymbolExtensions
{
private static readonly Func<IFieldSymbol, IFieldSymbol> CorrespondingTupleFieldAccessor;

static IFieldSymbolExtensions()
{
CorrespondingTupleFieldAccessor = LightupHelpers.CreateSyntaxPropertyAccessor<IFieldSymbol, IFieldSymbol>(typeof(IFieldSymbol), nameof(CorrespondingTupleField));
}

public static IFieldSymbol CorrespondingTupleField(this IFieldSymbol symbol)
{
return CorrespondingTupleFieldAccessor(symbol);
}
}
}
Loading

0 comments on commit e9c8dc5

Please sign in to comment.