Skip to content

Commit

Permalink
Implement SA1314
Browse files Browse the repository at this point in the history
  • Loading branch information
pdelvo committed Dec 29, 2015
1 parent 4ba45ef commit d82e12d
Show file tree
Hide file tree
Showing 11 changed files with 620 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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.NamingRules
{
using System.Collections.Immutable;
using System.Composition;
using System.Threading.Tasks;
using Helpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;

/// <summary>
/// Implements a code fix for diagnostics which are fixed by renaming a symbol to a set of given new names.
/// </summary>
/// <remarks>
/// <para>To fix a violation of this rule, change the name of the symbol so that it is one of the set of new names.</para>
/// </remarks>
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RenameToAnyCodeFixProvider))]
[Shared]
internal class RenameToAnyCodeFixProvider : CodeFixProvider
{
private static char[] comma = { ',' };

/// <inheritdoc/>
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(
SA1314ParametersShouldMatchInheritedNames.DiagnosticId);

/// <inheritdoc/>
public override FixAllProvider GetFixAllProvider()
{
return CustomFixAllProviders.BatchFixer;
}

/// <inheritdoc/>
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var document = context.Document;
var root = await document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

foreach (var diagnostic in context.Diagnostics)
{
var token = root.FindToken(diagnostic.Location.SourceSpan.Start);
if (string.IsNullOrEmpty(token.ValueText))
{
continue;
}

var originalName = token.ValueText;

var memberSyntax = RenameHelper.GetParentDeclaration(token);

SemanticModel semanticModel = await document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);

var declaredSymbol = semanticModel.GetDeclaredSymbol(memberSyntax);
if (declaredSymbol == null)
{
continue;
}

string[] newNames = diagnostic.Properties[nameof(newNames)].Split(comma);
foreach (var newName in newNames)
{
if (!await RenameHelper.IsValidNewMemberNameAsync(semanticModel, declaredSymbol, newName, context.CancellationToken).ConfigureAwait(false))
{
continue;
}

context.RegisterCodeFix(
CodeAction.Create(
string.Format(NamingResources.RenameToCodeFix, newName),
cancellationToken => RenameHelper.RenameSymbolAsync(document, root, token, newName, cancellationToken),
nameof(RenameToAnyCodeFixProvider) + newName),
diagnostic);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
<Compile Include="MaintainabilityRules\SA1410SA1411CodeFixProvider.cs" />
<Compile Include="MaintainabilityRules\SA1412CodeFixProvider.cs" />
<Compile Include="MaintainabilityRules\SA1412FixAllProvider.cs" />
<Compile Include="NamingRules\RenameToAnyCodeFixProvider.cs" />
<Compile Include="NamingRules\RenameToLowerCaseCodeFixProvider.cs" />
<Compile Include="NamingRules\RenameToUpperCaseCodeFixProvider.cs" />
<Compile Include="NamingRules\SA1302CodeFixProvider.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
// 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.Test.NamingRules
{
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using StyleCop.Analyzers.NamingRules;
using TestHelper;
using Xunit;
using Microsoft.CodeAnalysis;
public class SA1314UnitTests : CodeFixVerifier
{
[Fact]
public async Task TestThatDiagnosticIsNotReportedForMethodWithoutArgumentsAsync()
{
var testCode = @"public class TypeName
{
public void AMethod() { }
}";

await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsNotReportedForMethodWithoutInheritNamesAsync()
{
var testCode = @"public class TypeName
{
public void AMethod(string arg1, string arg2) { }
}";

await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsNotReportedForMethodWithoutInheritNamesAndArglistAsync()
{
var testCode = @"public class TypeName
{
public void AMethod(string arg1, string arg2, __arglist) { }
}";

await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsNotReportedForMethodWithoutInheritNamesAnParamsAsync()
{
var testCode = @"public class TypeName
{
public void AMethod(string arg1, string arg2, params string[] arg3) { }
}";

await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsReportedForMethodWithInheritNamesAsync()
{
var testCode = @"public class TypeName : BaseClass
{
public override void AMethod(string arg1, string arg2) { }
}
public abstract class BaseClass
{
public abstract void AMethod(string baseArg1, string baseArg2);
}";

var fixedCode = @"public class TypeName : BaseClass
{
public override void AMethod(string baseArg1, string baseArg2) { }
}
public abstract class BaseClass
{
public abstract void AMethod(string baseArg1, string baseArg2);
}";

var expected = new[]
{
this.CSharpDiagnostic().WithLocation(3, 41),
this.CSharpDiagnostic().WithLocation(3, 54)
};

await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpFixAsync(testCode, fixedCode, numberOfFixAllIterations: 2).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsReportedForMethodWithInheritNamesAndArglistAsync()
{
var testCode = @"public class TypeName : BaseClass
{
public override void AMethod(string arg1, string arg2, __arglist) { }
}
public abstract class BaseClass
{
public abstract void AMethod(string baseArg1, string baseArg2, __arglist);
}";

var fixedCode = @"public class TypeName : BaseClass
{
public override void AMethod(string baseArg1, string baseArg2, __arglist) { }
}
public abstract class BaseClass
{
public abstract void AMethod(string baseArg1, string baseArg2, __arglist);
}";

var expected = new[]
{
this.CSharpDiagnostic().WithLocation(3, 41),
this.CSharpDiagnostic().WithLocation(3, 54)
};

await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpFixAsync(testCode, fixedCode, numberOfFixAllIterations: 2).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsReportedForMethodWithInheritNamesAnParamsAsync()
{
var testCode = @"public class TypeName : BaseClass
{
public override void AMethod(string arg1, string arg2, params string[] arg3) { }
}
public abstract class BaseClass
{
public abstract void AMethod(string baseArg1, string baseArg2, params string[] baseArg3);
}";

var fixedCode = @"public class TypeName : BaseClass
{
public override void AMethod(string baseArg1, string baseArg2, params string[] baseArg3) { }
}
public abstract class BaseClass
{
public abstract void AMethod(string baseArg1, string baseArg2, params string[] baseArg3);
}";

var expected = new[]
{
this.CSharpDiagnostic().WithLocation(3, 41),
this.CSharpDiagnostic().WithLocation(3, 54),
this.CSharpDiagnostic().WithLocation(3, 76)
};

await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpFixAsync(testCode, fixedCode, numberOfFixAllIterations: 3).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsReportedForMethodWithInheritNamesWithInterfaceAsync()
{
var testCode = @"public class TypeName : IBase
{
public void AMethod(string arg1, string arg2) { }
}
public interface IBase
{
void AMethod(string baseArg1, string baseArg2);
}";

var fixedCode = @"public class TypeName : IBase
{
public void AMethod(string baseArg1, string baseArg2) { }
}
public interface IBase
{
void AMethod(string baseArg1, string baseArg2);
}";

var expected = new[]
{
this.CSharpDiagnostic().WithLocation(3, 32),
this.CSharpDiagnostic().WithLocation(3, 45)
};

await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpFixAsync(testCode, fixedCode, numberOfFixAllIterations: 2).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsReportedForMethodWithInheritNamesAndArglistWithInterfaceAsync()
{
var testCode = @"public class TypeName : IBase
{
public void AMethod(string arg1, string arg2, __arglist) { }
}
public interface IBase
{
void AMethod(string baseArg1, string baseArg2, __arglist);
}";

var fixedCode = @"public class TypeName : IBase
{
public void AMethod(string baseArg1, string baseArg2, __arglist) { }
}
public interface IBase
{
void AMethod(string baseArg1, string baseArg2, __arglist);
}";

var expected = new[]
{
this.CSharpDiagnostic().WithLocation(3, 32),
this.CSharpDiagnostic().WithLocation(3, 45)
};

await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpFixAsync(testCode, fixedCode, numberOfFixAllIterations: 2).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsReportedForMethodWithInheritNamesAnParamsWithInterfaceAsync()
{
var testCode = @"public class TypeName : IBase
{
public void AMethod(string arg1, string arg2, params string[] arg3) { }
}
public interface IBase
{
void AMethod(string baseArg1, string baseArg2, params string[] baseArg3);
}";

var fixedCode = @"public class TypeName : IBase
{
public void AMethod(string baseArg1, string baseArg2, params string[] baseArg3) { }
}
public interface IBase
{
void AMethod(string baseArg1, string baseArg2, params string[] baseArg3);
}";

var expected = new[]
{
this.CSharpDiagnostic().WithLocation(3, 32),
this.CSharpDiagnostic().WithLocation(3, 45),
this.CSharpDiagnostic().WithLocation(3, 67)
};

await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpFixAsync(testCode, fixedCode, numberOfFixAllIterations: 3).ConfigureAwait(false);
}

[Fact]
public async Task TestThatDiagnosticIsNotReportedForInvalidOverrideAsync()
{
var testCode = @"public class TypeName
{
public override void AMethod(string arg1, string arg2, params string[] arg3) { }
}";

var expected =
new DiagnosticResult
{
Id = "CS0115",
Severity = DiagnosticSeverity.Error,
Message = "'TypeName.AMethod(string, string, params string[])': no suitable method found to override",
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 3, 26) }
};

await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
}

protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
{
yield return new SA1314ParametersShouldMatchInheritedNames();
}

protected override CodeFixProvider GetCSharpCodeFixProvider()
{
return new RenameToAnyCodeFixProvider();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@
<Compile Include="NamingRules\SA1310UnitTests.cs" />
<Compile Include="NamingRules\SA1311UnitTests.cs" />
<Compile Include="NamingRules\SA1312UnitTests.cs" />
<Compile Include="NamingRules\SA1314UnitTests.cs" />
<Compile Include="NamingRules\SA1313UnitTests.cs" />
<Compile Include="NamingRules\SX1309SUnitTests.cs" />
<Compile Include="NamingRules\SX1309UnitTests.cs" />
Expand Down
Loading

0 comments on commit d82e12d

Please sign in to comment.