/// <inheritdoc/> protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContext context) { var document = context.Document; var syntaxRoot = await document.GetSyntaxRootAsync(context.CancellationToken) .ConfigureAwait(false); var semanticModel = await document.GetSemanticModelAsync(context.CancellationToken) .ConfigureAwait(false); foreach (var diagnostic in context.Diagnostics) { if (syntaxRoot.TryFindNodeOrAncestor(diagnostic, out MethodDeclarationSyntax methodDeclaration) && semanticModel.TryGetSymbol(methodDeclaration, context.CancellationToken, out var method)) { if (ClrMethod.IsAttachedGet(method, semanticModel, context.CancellationToken, out var fieldOrProperty) && DependencyProperty.TryGetRegisteredName(fieldOrProperty, semanticModel, context.CancellationToken, out var registeredName)) { var parameter = method.Parameters[0]; var text = StringBuilderPool.Borrow() .AppendLine($"/// <summary>Helper for getting <see cref=\"{fieldOrProperty.Name}\"/> from <paramref name=\"{parameter.Name}\"/>.</summary>") .AppendLine($"/// <param name=\"{parameter.Name}\"><see cref=\"{parameter.Type.ToMinimalDisplayString(semanticModel, methodDeclaration.SpanStart, SymbolDisplayFormat.MinimallyQualifiedFormat)}\"/> to read <see cref=\"{fieldOrProperty.Name}\"/> from.</param>") .AppendLine($"/// <returns>{registeredName} property value.</returns>") .Return(); context.RegisterCodeFix( "Add standard documentation.", (editor, _) => editor.ReplaceNode(methodDeclaration, x => x.WithDocumentationText(text)), this.GetType(), diagnostic); } else if (ClrMethod.IsAttachedSet(method, semanticModel, context.CancellationToken, out fieldOrProperty) && DependencyProperty.TryGetRegisteredName(fieldOrProperty, semanticModel, context.CancellationToken, out registeredName)) { var parameter = method.Parameters[0]; var text = StringBuilderPool.Borrow() .AppendLine($"/// <summary>Helper for setting <see cref=\"{fieldOrProperty.Name}\"/> on <paramref name=\"{parameter.Name}\"/>.</summary>") .AppendLine($"/// <param name=\"{parameter.Name}\"><see cref=\"{parameter.Type.ToMinimalDisplayString(semanticModel, methodDeclaration.SpanStart, SymbolDisplayFormat.MinimallyQualifiedFormat)}\"/> to set <see cref=\"{fieldOrProperty.Name}\"/> on.</param>") .AppendLine($"/// <param name=\"{method.Parameters[1].Name}\">{registeredName} property value.</param>") .Return(); context.RegisterCodeFix( "Add standard documentation.", (editor, _) => editor.ReplaceNode(methodDeclaration, x => x.WithDocumentationText(text)), this.GetType(), diagnostic); } } } }
private static void Handle(SyntaxNodeAnalysisContext context) { if (!context.IsExcludedFromAnalysis() && context.Node is MethodDeclarationSyntax methodDeclaration && context.ContainingSymbol is IMethodSymbol { IsStatic : true } method&& method.Parameters.TryElementAt(0, out var parameter) && parameter.Type.IsAssignableTo(KnownSymbols.DependencyObject, context.Compilation)) { if (ClrMethod.IsAttachedGet(methodDeclaration, context.SemanticModel, context.CancellationToken, out var getValueCall, out var fieldOrProperty)) { if (DependencyProperty.TryGetRegisteredName(fieldOrProperty, context.SemanticModel, context.CancellationToken, out _, out var registeredName)) { if (!method.Name.IsParts("Get", registeredName)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0004ClrMethodShouldMatchRegisteredName, methodDeclaration.Identifier.GetLocation(), ImmutableDictionary <string, string> .Empty.Add("ExpectedName", "Get" + registeredName), method.Name, "Get" + registeredName)); } if (method.DeclaredAccessibility.IsEither(Accessibility.Protected, Accessibility.Internal, Accessibility.Public) && !HasStandardText(methodDeclaration, fieldOrProperty, registeredName, out var location)) { context.ReportDiagnostic(Diagnostic.Create(Descriptors.WPF0061DocumentClrMethod, location)); } } if (DependencyProperty.TryGetRegisteredType(fieldOrProperty, context.SemanticModel, context.CancellationToken, out var registeredType) && !Equals(method.ReturnType, registeredType)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0013ClrMethodMustMatchRegisteredType, methodDeclaration.ReturnType.GetLocation(), "Return type", registeredType)); } if (Attribute.TryFind(methodDeclaration, KnownSymbols.AttachedPropertyBrowsableForTypeAttribute, context.SemanticModel, context.CancellationToken, out var attribute)) { if (attribute.TrySingleArgument(out var argument) && argument.Expression is TypeOfExpressionSyntax typeOf && TypeOf.TryGetType(typeOf, method.ContainingType, context.SemanticModel, context.CancellationToken, out var argumentType) && !argumentType.IsAssignableTo(parameter.Type, context.Compilation)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0034AttachedPropertyBrowsableForTypeAttributeArgument, argument.GetLocation(), parameter.Type.ToMinimalDisplayString( context.SemanticModel, argument.SpanStart))); } } else { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0033UseAttachedPropertyBrowsableForTypeAttribute, methodDeclaration.Identifier.GetLocation(), parameter.Type.ToMinimalDisplayString(context.SemanticModel, methodDeclaration.SpanStart))); } if (methodDeclaration.Body is { } body&& TryGetSideEffect(body, getValueCall, out var sideEffect)) { context.ReportDiagnostic(Diagnostic.Create(Descriptors.WPF0042AvoidSideEffectsInClrAccessors, sideEffect.GetLocation())); } }
private static void Handle(SyntaxNodeAnalysisContext context) { if (!context.IsExcludedFromAnalysis() && context.Node is MethodDeclarationSyntax methodDeclaration && context.ContainingSymbol is IMethodSymbol { IsStatic : true } method&& method.Parameters.TryElementAt(0, out var element) && element.Type.IsAssignableTo(KnownSymbols.DependencyObject, context.Compilation)) { if (ClrMethod.IsAttachedGet(methodDeclaration, context.SemanticModel, context.CancellationToken, out var getValueCall, out var backing)) { if (DependencyProperty.TryGetRegisteredName(backing, context.SemanticModel, context.CancellationToken, out _, out var registeredName)) { if (!method.Name.IsParts("Get", registeredName)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0004ClrMethodShouldMatchRegisteredName, methodDeclaration.Identifier.GetLocation(), ImmutableDictionary <string, string> .Empty.Add("ExpectedName", "Get" + registeredName), method.Name, "Get" + registeredName)); } if (method.DeclaredAccessibility.IsEither(Accessibility.Protected, Accessibility.Internal, Accessibility.Public)) { var summaryFormat = "<summary>Helper for getting <see cref=\"{backing}\"/> from <paramref name=\"{element}\"/>.</summary>"; var paramFormat = "<param name=\"{element}\"><see cref=\"{element.type}\"/> to read <see cref=\"{backing}\"/> from.</param>"; var returnsFormat = "<returns>{registered_name} property value.</returns>"; if (methodDeclaration.TryGetDocumentationComment(out var comment)) { if (comment.VerifySummary(summaryFormat, backing.Symbol.Name, element.Name) is { } summaryError) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0061DocumentClrMethod, summaryError.Location, ImmutableDictionary <string, string> .Empty.Add(nameof(DocComment), summaryError.Text))); } if (comment.VerifyParameter(paramFormat, element, element.ToCrefType(), backing.Symbol.Name) is { } paramError) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0061DocumentClrMethod, paramError.Location, ImmutableDictionary <string, string> .Empty.Add(nameof(DocComment), paramError.Text))); } if (comment.VerifyReturns(returnsFormat, registeredName) is { } returnsError) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0061DocumentClrMethod, returnsError.Location, ImmutableDictionary <string, string> .Empty.Add(nameof(DocComment), returnsError.Text))); } } else { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0061DocumentClrMethod, methodDeclaration.Identifier.GetLocation(), ImmutableDictionary <string, string> .Empty.Add( nameof(DocComment), $"/// {DocComment.Format(summaryFormat, backing.Symbol.Name, element.Name)}\n" + $"/// {DocComment.Format(paramFormat, element.Name, element.ToCrefType(), backing.Name)}\n" + $"/// {DocComment.Format(returnsFormat, registeredName)}\n"))); } } } if (DependencyProperty.TryGetRegisteredType(backing, context.SemanticModel, context.CancellationToken, out var registeredType) && !Equals(method.ReturnType, registeredType)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0013ClrMethodMustMatchRegisteredType, methodDeclaration.ReturnType.GetLocation(), "Return type", registeredType)); } if (Gu.Roslyn.AnalyzerExtensions.Attribute.TryFind(methodDeclaration, KnownSymbols.AttachedPropertyBrowsableForTypeAttribute, context.SemanticModel, context.CancellationToken, out var attribute)) { if (attribute.TrySingleArgument(out var argument) && argument.Expression is TypeOfExpressionSyntax typeOf && TypeOf.TryGetType(typeOf, method.ContainingType, context.SemanticModel, context.CancellationToken, out var argumentType) && !argumentType.IsAssignableTo(element.Type, context.Compilation)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0034AttachedPropertyBrowsableForTypeAttributeArgument, argument.GetLocation(), element.Type.ToMinimalDisplayString( context.SemanticModel, argument.SpanStart))); } } else { context.ReportDiagnostic( Diagnostic.Create( Descriptors.WPF0033UseAttachedPropertyBrowsableForTypeAttribute, methodDeclaration.Identifier.GetLocation(), element.Type.ToMinimalDisplayString(context.SemanticModel, methodDeclaration.SpanStart))); } if (methodDeclaration.Body is { } body&& TryGetSideEffect(body, getValueCall, out var sideEffect)) { context.ReportDiagnostic(Diagnostic.Create(Descriptors.WPF0042AvoidSideEffectsInClrAccessors, sideEffect.GetLocation())); } }