I converted a C# analyzer for removing empty argument lists from attributes to be a C# and VB.NET analyzer:
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeCSharpSymbol, CSharpSyntaxKind.Attribute);
context.RegisterSyntaxNodeAction(AnalyzeVisualBasicSymbol, VisualBasicSyntaxKind.Attribute);
}
private void AnalyzeCSharpSymbol(SyntaxNodeAnalysisContext context)
{
var attributeExpression = context.Node as CSharpAttributeSyntax;
// attribute must have arguments
// if there are no parenthesis, the ArgumentList is null
// if there are empty parenthesis, the ArgumentList is empty
if (attributeExpression?.ArgumentList == null || attributeExpression.ArgumentList.Arguments.Any())
{
return;
}
context.ReportDiagnostic(Diagnostic.Create(Rule, attributeExpression.GetLocation()));
}
private void AnalyzeVisualBasicSymbol(SyntaxNodeAnalysisContext context)
{
var attributeExpression = context.Node as VisualBasicAttributeSyntax;
// attribute must have arguments
// if there are no parenthesis, the ArgumentList is null
// if there are empty parenthesis, the ArgumentList is empty
if (attributeExpression?.ArgumentList == null || attributeExpression.ArgumentList.Arguments.Any())
{
return;
}
context.ReportDiagnostic(Diagnostic.Create(Rule, attributeExpression.GetLocation()));
}
Here are my custom using
definitions for this class:
using CSharpSyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind;
using VisualBasicSyntaxKind = Microsoft.CodeAnalysis.VisualBasic.SyntaxKind;
using CSharpAttributeSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax;
using VisualBasicAttributeSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.AttributeSyntax;
This seems very WET, but after thinking it over, this seems to be the best way because only what these methods do is the same, the details are just a coincidence.
This is the code fix. Again, I provided overloaded methods:
private Task<Solution> RemoveEmptyArgumentListAsync(Document document, SyntaxNode root, SyntaxNode statement)
{
SyntaxNode newRoot = null;
if (statement is Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax)
{
newRoot = RemoveEmptyArgumentList(root, (Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax)statement);
}
else if (statement is Microsoft.CodeAnalysis.VisualBasic.Syntax.AttributeSyntax)
{
newRoot = RemoveEmptyArgumentList(root, (Microsoft.CodeAnalysis.VisualBasic.Syntax.AttributeSyntax)statement);
}
var newDocument = document.WithSyntaxRoot(newRoot);
return Task.FromResult(newDocument.Project.Solution);
}
private SyntaxNode RemoveEmptyArgumentList(SyntaxNode root, Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax attributeExpression)
{
return root.RemoveNode(attributeExpression.ArgumentList, SyntaxRemoveOptions.KeepNoTrivia);
}
private SyntaxNode RemoveEmptyArgumentList(SyntaxNode root, Microsoft.CodeAnalysis.VisualBasic.Syntax.AttributeSyntax attributeExpression)
{
return root.RemoveNode(attributeExpression.ArgumentList, SyntaxRemoveOptions.KeepNoTrivia);
}
And more custom using
s:
using CSharpAttributeSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax;
using VisualBasicAttributeSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.AttributeSyntax;
And here are some sample unit tests:
C#
[TestMethod]
public void AttributeWithEmptyArgumentList_AttributeWithEmptyArgumentList()
{
var original = @"
using System;
namespace ConsoleApplication1
{
class MyClass
{
[Obsolete()]
void Method(string input)
{
}
}
}";
var result = @"
using System;
namespace ConsoleApplication1
{
class MyClass
{
[Obsolete]
void Method(string input)
{
}
}
}";
VerifyDiagnostic(original, AttributeWithEmptyArgumentListAnalyzer.Rule.MessageFormat.ToString());
VerifyFix(original, result);
}
Visual Basic
[TestMethod]
public void AttributeWithEmptyArgumentList_AttributeWithEmptyArgumentList()
{
var original = @"
Module Module1
<Obsolete()>
Sub Foo()
End Sub
End Module";
var result = @"
Module Module1
<Obsolete>
Sub Foo()
End Sub
End Module";
VerifyDiagnostic(original, AttributeWithEmptyArgumentListAnalyzer.Rule.MessageFormat.ToString());
VerifyFix(original, result);
}
For reference, these are the test cases that must pass:
Attributes with parameters (no results):
[Obsolete("test")]
and <Obsolete("test")>
Attributes without parameters or parenthesis (no results):
[Obsolete]
and <Obsolete>
Attributes without parameters and with parenthesis (results):
[Obsolete()]
and <Obsolete()>
CSharpAttributeSyntax
andVisualBasicAttributeSyntax
? Is this part of Roslyn? – EBrown Sep 11 at 13:34