private static void AnalyzeSummaryElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation, PropertyDeclarationSyntax propertyDeclaration, string startingTextGets, string startingTextSets, string startingTextGetsOrSets) { var diagnosticProperties = ImmutableDictionary.CreateBuilder<string, string>(); ArrowExpressionClauseSyntax expressionBody = propertyDeclaration.ExpressionBody; AccessorDeclarationSyntax getter = null; AccessorDeclarationSyntax setter = null; if (propertyDeclaration.AccessorList != null) { foreach (var accessor in propertyDeclaration.AccessorList.Accessors) { switch (accessor.Keyword.Kind()) { case SyntaxKind.GetKeyword: getter = accessor; break; case SyntaxKind.SetKeyword: setter = accessor; break; } } } XmlElementSyntax summaryElement = (XmlElementSyntax)syntax; if (summaryElement == null) { // This is reported by SA1604. return; } // Add a no code fix tag when the summary element is empty. // This will only impact SA1623, because SA1624 cannot trigger with an empty summary. if (summaryElement.Content.Count == 0) { diagnosticProperties.Add(NoCodeFixKey, string.Empty); } var textElement = summaryElement.Content.FirstOrDefault() as XmlTextSyntax; var text = textElement == null ? string.Empty : XmlCommentHelper.GetText(textElement, true).TrimStart(); if (getter != null || expressionBody != null) { bool startsWithGetOrSet = text.StartsWith(startingTextGetsOrSets, StringComparison.Ordinal); if (setter != null) { // There is no way getter is null (can't have expression body and accessor list) bool getterVisible; bool setterVisible; if (!getter.Modifiers.Any() && !setter.Modifiers.Any()) { // Case 1: The getter and setter have the same declared accessibility getterVisible = true; setterVisible = true; } else if (getter.Modifiers.Any(SyntaxKind.PrivateKeyword)) { // Case 3 getterVisible = false; setterVisible = true; } else if (setter.Modifiers.Any(SyntaxKind.PrivateKeyword)) { // Case 3 getterVisible = true; setterVisible = false; } else { var propertyAccessibility = propertyDeclaration.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken); bool propertyOnlyInternal = propertyAccessibility == Accessibility.Internal || propertyAccessibility == Accessibility.ProtectedAndInternal || propertyAccessibility == Accessibility.Private; if (propertyOnlyInternal) { // Case 2: Property only internal and no accessor is explicitly private getterVisible = true; setterVisible = true; } else { var getterAccessibility = getter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken); var setterAccessibility = setter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken); switch (getterAccessibility) { case Accessibility.Public: case Accessibility.ProtectedOrInternal: case Accessibility.Protected: // Case 4 getterVisible = true; break; case Accessibility.Internal: case Accessibility.ProtectedAndInternal: case Accessibility.Private: default: // The property is externally accessible, so the setter must be more accessible. getterVisible = false; break; } switch (setterAccessibility) { case Accessibility.Public: case Accessibility.ProtectedOrInternal: case Accessibility.Protected: // Case 4 setterVisible = true; break; case Accessibility.Internal: case Accessibility.ProtectedAndInternal: case Accessibility.Private: default: // The property is externally accessible, so the getter must be more accessible. setterVisible = false; break; } } } if (getterVisible && !setterVisible) { if (startsWithGetOrSet) { diagnosticProperties.Add(ExpectedTextKey, startingTextGets); diagnosticProperties.Add(TextToRemoveKey, startingTextGetsOrSets); context.ReportDiagnostic(Diagnostic.Create(SA1624Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), "get", startingTextGets)); } else if (!text.StartsWith(startingTextGets, StringComparison.Ordinal)) { diagnosticProperties.Add(ExpectedTextKey, startingTextGets); context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGets)); } } else if (!getterVisible && setterVisible) { if (startsWithGetOrSet) { diagnosticProperties.Add(ExpectedTextKey, startingTextSets); diagnosticProperties.Add(TextToRemoveKey, startingTextGetsOrSets); context.ReportDiagnostic(Diagnostic.Create(SA1624Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), "set", startingTextSets)); } else if (!text.StartsWith(startingTextSets, StringComparison.Ordinal)) { diagnosticProperties.Add(ExpectedTextKey, startingTextSets); context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextSets)); } } else { if (!startsWithGetOrSet) { diagnosticProperties.Add(ExpectedTextKey, startingTextGetsOrSets); context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGetsOrSets)); } } } else { if (startsWithGetOrSet || !text.StartsWith(startingTextGets, StringComparison.Ordinal)) { diagnosticProperties.Add(ExpectedTextKey, startingTextGets); context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGets)); } } } else if (setter != null) { if (!text.StartsWith(startingTextSets, StringComparison.Ordinal)) { diagnosticProperties.Add(ExpectedTextKey, startingTextSets); context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextSets)); } } }