An analyzer that a property notifies INPC correctly. Given the following code:
public class C : INotifyPropertyChanged
{
private int p;
public event PropertyChangedEventHandler PropertyChanged;
public int P
{
get => this.p;
set
{
if (value == this.p)
{
return;
}
this.p = value;
this.OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The analyzer registers context.RegisterSyntaxNodeAction(c => Handle(c), SyntaxKind.PropertyDeclaration);
and checks the setter:
- Walks the setter to find all
InvpocationExpressionSyntax
. And for each:- Gets the
IMethodSymbol
of theInvpocationExpressionSyntax
- Get the
MethodDeclarationSyntax
for theIMethodSymbol
viaSyntaxReference
if it exits. - Walks the method declaration to see that it notifies correctly for the expected property.
- Gets the
The problem above is that in 1.3 there are no guarantees that the SemanticModel
can be used. If the MethodDeclarationSyntax
is in a base class or partial in another document, using the semantic model will throw.
This is easily solved, we check if (semanticmodel.SyntaxTree != declaration.SyntaxTree)
and call Compilation.GetSemanticModel(declaration.SyntaxTree)
to get a model we can use.
GetSemanticModel
is an expensive call. I solved this by adding memoization. Memoization is nontrivial as the cache needs to be emptied on compilation end and even then there may be memory issues in keeping syntax trees alive. I implemented this via an analyzer with RegisterCompilationStartAction
and finalizer and refcounting, pretty messy. The performance improvements are great, order of magnitude but Cyrus is worried about the implementation and rightfully so.
It is not ideal to have analyzers do this caching.
- Have an API for getting symbols for nodes in any tree.
- Provide memoization API
- Optimize
GetSemanticModel