Skip to content

Commit

Permalink
Merge pull request #113 from Hosch250/master
Browse files Browse the repository at this point in the history
Close issue #20 - Find and remove public empty ctor's with no comments and no attributes.
  • Loading branch information
Vannevelj committed Aug 5, 2015
2 parents c3d0304 + 6c9a707 commit 4f223a9
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RoslynTester.Helpers.CSharp;
using VSDiagnostics.Diagnostics.General.SingleEmptyConstructor;

namespace VSDiagnostics.Test.Tests.General
{
[TestClass]
public class SingleEmptyConstructorAnalyzerTests : CSharpCodeFixVerifier
{
protected override DiagnosticAnalyzer DiagnosticAnalyzer => new SingleEmptyConstructorAnalyzer();

protected override CodeFixProvider CodeFixProvider => new SingleEmptyConstructorCodeFix();

[TestMethod]
public void SingleEmptyConstructorAnalyzer_WithEmptyConstructor_InvokesWarning ()
{
var original = @"
namespace ConsoleApplication1
{
public class MyClass
{
public MyClass()
{
}
}
}";

var result = @"
namespace ConsoleApplication1
{
public class MyClass
{
}
}";

VerifyDiagnostic (original, string.Format(SingleEmptyConstructorAnalyzer.Rule.MessageFormat.ToString(), "MyClass"));
VerifyFix(original, result);
}

[TestMethod]
public void SingleEmptyConstructorAnalyzer_WithSingleLineCommentInConstructor_DoesNotInvokeWarning ()
{
var original = @"
namespace ConsoleApplication1
{
public class MyClass
{
public MyClass()
{
// ctor has comment
}
}
}";

VerifyDiagnostic(original);
}

[TestMethod]
public void SingleEmptyConstructorAnalyzer_WithMultiLineCommentInConstructor_DoesNotInvokeWarning ()
{
var original = @"
namespace ConsoleApplication1
{
public class MyClass
{
public MyClass()
{
/* ctor has comment
ctor has multiline comment */
}
}
}";

VerifyDiagnostic(original);
}

[TestMethod]
public void SingleEmptyConstructorAnalyzer_WithConstructorParameters_DoesNotInvokeWarning ()
{
var original = @"
namespace ConsoleApplication1
{
public class MyClass
{
public MyClass(int foo)
{
}
}
}";

VerifyDiagnostic(original);
}

[TestMethod]
public void SingleEmptyConstructorAnalyzer_WithConstructorBody_DoesNotInvokeWarning ()
{
var original = @"
namespace ConsoleApplication1
{
public class MyClass
{
public int Foo { get; }
public MyClass()
{
Foo = 0;
}
}
}";

VerifyDiagnostic(original);
}

[TestMethod]
public void SingleEmptyConstructorAnalyzer_WithImplicitPrivateConstructor_DoesNotInvokeWarning ()
{
var original = @"
namespace ConsoleApplication1
{
public class MyClass
{
public int Foo { get; }
MyClass()
{
Foo = 0;
}
}
}";

VerifyDiagnostic(original);
}

[TestMethod]
public void SingleEmptyConstructorAnalyzer_WithExplicitInternalConstructor_DoesNotInvokeWarning ()
{
var original = @"
namespace ConsoleApplication1
{
public class MyClass
{
public int Foo { get; }
internal MyClass()
{
Foo = 0;
}
}
}";

VerifyDiagnostic(original);
}

[TestMethod]
public void SingleEmptyConstructorAnalyzer_ConstructorHasAttributes_DoesNotInvokeWarning ()
{
var original = @"
using System;
namespace ConsoleApplication1
{
public class MyClass
{
[Obsolete]
public MyClass()
{
}
}
}";

VerifyDiagnostic(original);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
<Compile Include="Tests\General\NullableToShorthandAnalyzerTests.cs" />
<Compile Include="Tests\General\OnPropertyChangedWithoutNameOfOperatorAnalyzerTests.cs" />
<Compile Include="Tests\General\SimplifyExpressionBodiedMemberAnalyzerTests.cs" />
<Compile Include="Tests\General\SingleEmptyConstructorAnalyzerTests.cs" />
<Compile Include="Tests\General\TryCastUsingAsNotNullInsteadOfIsAsAnalyzerTests.cs" />
<Compile Include="Tests\General\TypeToVarAnalyzerTests.cs" />
<Compile Include="Tests\Strings\ReplaceEmptyStringWithStringDotEmptyTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using VSDiagnostics.Utilities;

namespace VSDiagnostics.Diagnostics.General.SingleEmptyConstructor
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
class SingleEmptyConstructorAnalyzer : DiagnosticAnalyzer
{
private const string Category = "General";
private const string DiagnosticId = nameof(SingleEmptyConstructorAnalyzer);
private const string Message = "Type \"{0}\" has a redundant default constructor.";
private const DiagnosticSeverity Severity = DiagnosticSeverity.Warning;
private const string Title = "Your constructor is the same as a default constructor and can be removed.";

internal static DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, Severity, true);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

public override void Initialize (AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeSymbol, SyntaxKind.ConstructorDeclaration);
}

private void AnalyzeSymbol (SyntaxNodeAnalysisContext context)
{
var ctorExpression = context.Node as ConstructorDeclarationSyntax;
if (ctorExpression == null)
{
return;
}

// ctor must be public
if (!ctorExpression.Modifiers.Any(SyntaxKind.PublicKeyword))
{
return;
}

// ctor must not have parameters
if (ctorExpression.ParameterList.Parameters.Any())
{
return;
}

// ctor must have no body statements
if (ctorExpression.Body.Statements.Any())
{
return;
}

// ctor must not contain comments
if (ctorExpression.Body.CloseBraceToken.LeadingTrivia.Any(t => t.IsCommentTrivia()))
{
return;
}

// ctor must not have attributes
if (ctorExpression.AttributeLists.Any())
{
return;
}

context.ReportDiagnostic(Diagnostic.Create(Rule, ctorExpression.GetLocation(), ctorExpression.Identifier));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using System.Composition;
using System.Collections.Immutable;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace VSDiagnostics.Diagnostics.General.SingleEmptyConstructor
{
[ExportCodeFixProvider("SingleEmptyConstructorCodeFix", LanguageNames.CSharp), Shared]
class SingleEmptyConstructorCodeFix : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(SingleEmptyConstructorAnalyzer.Rule.Id);

public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;

var statement = root.FindNode(diagnosticSpan);
context.RegisterCodeFix(CodeAction.Create("Simplify expression", x => RemoveConstructorAsync(context.Document, root, statement), nameof(SingleEmptyConstructorAnalyzer)), diagnostic);
}

private Task<Solution> RemoveConstructorAsync(Document document, SyntaxNode root, SyntaxNode statement)
{
var ctorDeclarationExpression = (ConstructorDeclarationSyntax) statement;
var newRoot = root.RemoveNode(ctorDeclarationExpression, SyntaxRemoveOptions.KeepNoTrivia);

var newDocument = document.WithSyntaxRoot(newRoot);
return Task.FromResult(newDocument.Project.Solution);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
<Compile Include="Diagnostics\General\OnPropertyChangedWithoutNameOfOperator\OnPropertyChangedWithoutNameOfOperatorCodeFix.cs" />
<Compile Include="Diagnostics\General\SimplifyExpressionBodiedMember\SimplifyExpressionBodiedMemberAnalyzer.cs" />
<Compile Include="Diagnostics\General\SimplifyExpressionBodiedMember\SimplifyExpressionBodiedMemberCodeFix.cs" />
<Compile Include="Diagnostics\General\SingleEmptyConstructor\SingleEmptyConstructorAnalyzer.cs" />
<Compile Include="Diagnostics\General\SingleEmptyConstructor\SingleEmptyConstructorCodeFix.cs" />
<Compile Include="Diagnostics\General\TryCastWithoutUsingAsNotNull\TryCastWithoutUsingAsNotNullAnalyzer.cs" />
<Compile Include="Diagnostics\General\TryCastWithoutUsingAsNotNull\TryCastWithoutUsingAsNotNullCodeFix.cs" />
<Compile Include="Diagnostics\General\TypeToVar\TypeToVarAnalyzer.cs" />
Expand Down

0 comments on commit 4f223a9

Please sign in to comment.