-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #113 from Hosch250/master
Close issue #20 - Find and remove public empty ctor's with no comments and no attributes.
- Loading branch information
Showing
5 changed files
with
287 additions
and
0 deletions.
There are no files selected for viewing
176 changes: 176 additions & 0 deletions
176
...ics/VSDiagnostics/VSDiagnostics.Test/Tests/General/SingleEmptyConstructorAnalyzerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
...SDiagnostics/Diagnostics/General/SingleEmptyConstructor/SingleEmptyConstructorAnalyzer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
...VSDiagnostics/Diagnostics/General/SingleEmptyConstructor/SingleEmptyConstructorCodeFix.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters