Skip to content

Instantly share code, notes, and snippets.

@kkolodziejczak
Created March 17, 2019 18:54
Show Gist options
  • Select an option

  • Save kkolodziejczak/65d1424485fa417377c2a21108c7b316 to your computer and use it in GitHub Desktop.

Select an option

Save kkolodziejczak/65d1424485fa417377c2a21108c7b316 to your computer and use it in GitHub Desktop.
Make sure that all classes that inherit BasePageViewModel invoke Initialize() in the constructor.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
using System.Linq;
namespace Analyzer1
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class BasePageViewModelAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "XF001";
public static readonly LocalizableString Title = "Classes inheriting BasePageViewModel need to invoke Initialize() method.";
public static readonly LocalizableString MessageFormat = "Classes inheriting BasePageViewModel need to invoke Initialize() method after they are ready.";
internal static readonly LocalizableString Description = "Classes inheriting BasePageViewModel need to invoke Initialize() method after they are ready.";
internal const string Category = "XamarinFramework";
internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxTreeAction(CheckIfConstructorContainsInitializeMethod);
}
private void CheckIfConstructorContainsInitializeMethod(SyntaxTreeAnalysisContext context)
{
if (context.Tree.GetRoot(context.CancellationToken) is CompilationUnitSyntax root)
{
var wrongClasses = root
.DescendantNodes()
.OfType<ConstructorDeclarationSyntax>()
//Check for BasePageViewModel BaseClass
.Where(ctor =>
{
var declaration = (ClassDeclarationSyntax)ctor.Parent;
return declaration.BaseList?.Types
.Any(typ =>
string.CompareOrdinal(((IdentifierNameSyntax)typ.Type).Identifier.Text, "BasePageViewModel") == 0) ?? false;
})
// Check if there is no Initialize message
.Where(ctor =>
{
var containsInit = ctor.Body.Statements
.OfType<ExpressionStatementSyntax>()
.Any(f =>
{
if (f.Expression is InvocationExpressionSyntax ies)
{
if (ies.Expression is IdentifierNameSyntax ins)
{
return string.CompareOrdinal(ins.Identifier.Text, "Initialize") == 0;
}
}
return false;
});
return !containsInit;
});
foreach (var wrongClass in wrongClasses)
{
var locations = wrongClass.GetLocation();
var diagnostic = Diagnostic.Create(Rule, locations);
context.ReportDiagnostic(diagnostic);
}
}
}
}
}
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Analyzer1
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(Analyzer1CodeFixProvider)), Shared]
public class BasePageViewModelCodeFixProvider : CodeFixProvider
{
private const string title = "Invoke Initialize";
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(BasePageViewModelAnalyzer.DiagnosticId);
public sealed override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}
public sealed 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;
// Find the constructor declaration identified by the diagnostic.
var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<ConstructorDeclarationSyntax>().First();
// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeAction.Create(
title: title,
createChangedDocument: c => InvokeInitializeAsync(context.Document, declaration, c),
equivalenceKey: title),
diagnostic);
}
private async Task<Document> InvokeInitializeAsync(Document contextDocument, ConstructorDeclarationSyntax declaration, CancellationToken cancellationToken)
{
var root = await contextDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var initializeStatement = SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(
SyntaxFactory.IdentifierName("Initialize")));
var newRoot = root.ReplaceNode(declaration, declaration.AddBodyStatements(initializeStatement));
return contextDocument.WithSyntaxRoot(newRoot);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment