Skip to content

Commit

Permalink
Merge e62e3c0 into b0f9bd0
Browse files Browse the repository at this point in the history
  • Loading branch information
carloscds committed Jan 22, 2015
2 parents b0f9bd0 + e62e3c0 commit b909573
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/CSharp/CodeCracker/CodeCracker.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
<Compile Include="Refactoring\ParameterRefactoryCodeFixProvider.cs" />
<Compile Include="Style\AlwaysUseVarAnalyzer.cs" />
<Compile Include="Style\AlwaysUseVarCodeFixProvider.cs" />
<Compile Include="Style\TaskNameAsyncCodeFixProvider.cs" />
<Compile Include="Style\TaskNameAsyncAnalyzer.cs" />
<Compile Include="Style\PropertyPrivateSetAnalyzer.cs" />
<Compile Include="Style\PropertyPrivateSetCodeFixProvider.cs" />
<Compile Include="Style\RemoveCommentedCodeAnalyzer.cs" />
Expand Down
60 changes: 60 additions & 0 deletions src/CSharp/CodeCracker/Style/TaskNameAsyncAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
using System.Threading.Tasks;

namespace CodeCracker.Style
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class TaskNameAsyncAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "CC0061";
internal const string Title = "Async method can be terminating with 'Async' name.";
internal const string MessageFormat = "Change method name to {0}";
internal const string Category = SupportedCategories.Style;
const string Description = "Async method can be terminating with 'Async' name.";

internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(
DiagnosticId,
Title,
MessageFormat,
Category,
DiagnosticSeverity.Info,
isEnabledByDefault: true,
description: Description,
helpLink: HelpLink.ForDiagnostic(DiagnosticId));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }

public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeMethod, SyntaxKind.MethodDeclaration);
}

private static void AnalyzeMethod(SyntaxNodeAnalysisContext context)
{
var method = (MethodDeclarationSyntax)context.Node;
if (method.Identifier.ToString().EndsWith("Async")) return;

var errorMessage = method.Identifier.ToString() + "Async";
var diag = Diagnostic.Create(Rule, method.GetLocation(), errorMessage);

if (method.Modifiers.Any(SyntaxKind.AsyncKeyword))
{
context.ReportDiagnostic(diag);
return;
}
var returnType = context.SemanticModel.GetSymbolInfo(method.ReturnType).Symbol as INamedTypeSymbol;
if (returnType == null) return;

if (returnType.ToString() == "System.Threading.Tasks.Task" ||
(returnType.IsGenericType && returnType.ConstructedFrom.ToString() == "System.Threading.Tasks.Task<TResult>"))
{
context.ReportDiagnostic(diag);
}

}
}
}
47 changes: 47 additions & 0 deletions src/CSharp/CodeCracker/Style/TaskNameAsyncCodeFixProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Rename;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace CodeCracker.Style
{
[ExportCodeFixProvider("CodeCrackerTaskNameAsyncCodeFixProvider", LanguageNames.CSharp), Shared]
public class TaskNameAsyncCodeFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> GetFixableDiagnosticIds() => ImmutableArray.Create(TaskNameAsyncAnalyzer.DiagnosticId);

public sealed override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}

public sealed override async Task ComputeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<MethodDeclarationSyntax>().First();
context.RegisterFix(CodeAction.Create("Change method name including 'Async'.", c => ChangeMethodNameAsync(context.Document, declaration, c)), diagnostic);
}

private async Task<Solution> ChangeMethodNameAsync(Document document, MethodDeclarationSyntax methodStatement, CancellationToken cancellationToken)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
var newName = methodStatement.Identifier.ToString()+"Async";
var solution = document.Project.Solution;
var symbol = semanticModel.GetDeclaredSymbol(methodStatement, cancellationToken);
var options = solution.Workspace.Options;
var newSolution = await Renamer.RenameSymbolAsync(solution, symbol, newName,
options, cancellationToken).ConfigureAwait(false);
return newSolution;
}
}
}
1 change: 1 addition & 0 deletions test/CSharp/CodeCracker.Test/CodeCracker.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
<Compile Include="Style\StringFormatTests.cs" />
<Compile Include="Style\AlwaysUseVarTests.cs" />
<Compile Include="Style\RemoveCommentedCodeTests.cs" />
<Compile Include="Style\TaskNameASyncTests.cs" />
<Compile Include="Usage\AbstractClassShouldNotHavePublicCtorTests.cs" />
<Compile Include="Usage\ArgumentExceptionTests.cs" />
<Compile Include="Style\ConvertToSwitchTests.cs" />
Expand Down
149 changes: 149 additions & 0 deletions test/CSharp/CodeCracker.Test/Style/TaskNameASyncTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using CodeCracker.Style;
using Microsoft.CodeAnalysis;
using System.Threading.Tasks;
using TestHelper;
using Xunit;

namespace CodeCracker.Test.Style
{
public class TaskNameAsyncTests : CodeFixTest<TaskNameAsyncAnalyzer, TaskNameAsyncCodeFixProvider>
{
[Fact]
public async Task TaskNameAsyncMethodCorrect()
{
const string source = @"
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class Foo
{
Task TestAsync() {};
}
}";
await VerifyCSharpHasNoDiagnosticsAsync(source);
}

[Fact]
public async Task TaskNameAsyncMethodWhithoutAsyncName()
{
const string source = @"
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class Foo
{
Task Test() {};
}
}";
var expected = new DiagnosticResult
{
Id = TaskNameAsyncAnalyzer.DiagnosticId,
Message = string.Format(TaskNameAsyncAnalyzer.MessageFormat,"TestAsync"),
Severity = DiagnosticSeverity.Info,
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 8, 13) }
};
await VerifyCSharpDiagnosticAsync(source, expected);
}

[Fact]
public async Task ChangeTaskNameWhithoutAsync()
{
const string source = @"
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class Foo
{
Task Test() {};
}
}";
const string fixtest = @"
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class Foo
{
Task TestAsync() {};
}
}";
await VerifyCSharpFixAsync(source, fixtest);
}


[Fact]
public async Task ChangeTaskNameWhithoutAsyncAndClassImplementation()
{
const string source = @"
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class Foo
{
public methodTest()
{
await Test();
}
public Task<bool> Test()
{
return true;
}
}
}";
const string fixtest = @"
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class Foo
{
public methodTest()
{
await TestAsync();
}
public Task<bool> TestAsync()
{
return true;
}
}
}";
await VerifyCSharpFixAsync(source, fixtest);
}

[Fact]
public async Task ChangeTaskNameWhithoutAsyncAndInterfaceImplementation()
{
const string source = @"
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public interface IFoo
{
public Task Test()
}
}";
const string fixtest = @"
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public interface IFoo
{
public Task TestAsync()
}
}";
await VerifyCSharpFixAsync(source, fixtest);
}
}
}

0 comments on commit b909573

Please sign in to comment.