public static void HandlePropertyDeclaration(SyntaxNodeAnalysisContext context, StyleCopSettings settings) { if (context.GetDocumentationMode() == DocumentationMode.None) { return; } PropertyDeclarationSyntax declaration = (PropertyDeclarationSyntax)context.Node; Accessibility declaredAccessibility = declaration.GetDeclaredAccessibility(context.SemanticModel, context.CancellationToken); Accessibility effectiveAccessibility = declaration.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken); if (NeedsComment(settings.DocumentationRules, declaration.Kind(), declaration.Parent.Kind(), declaredAccessibility, effectiveAccessibility)) { if (!XmlCommentHelper.HasDocumentation(declaration)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.Identifier.GetLocation())); } } }
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 = syntax as XmlElementSyntax; if (summaryElement == null) { // This is reported by SA1604 or SA1606. 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(); bool startsWithGetOrSet = text.StartsWith(startingTextGetsOrSets, StringComparison.OrdinalIgnoreCase); bool getterVisible, setterVisible; if (getter != null && setter != null) { 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; } } } } else { if (getter != null || expressionBody != null) { getterVisible = true; setterVisible = false; } else { getterVisible = false; setterVisible = setter != null; } } if (getterVisible) { if (setterVisible) { if (!startsWithGetOrSet) { diagnosticProperties.Add(ExpectedTextKey, startingTextGetsOrSets); if (text.StartsWith(startingTextGets, StringComparison.OrdinalIgnoreCase)) { diagnosticProperties.Add(TextToRemoveKey, text.Substring(0, startingTextGets.Length)); } else if (text.StartsWith(startingTextSets, StringComparison.OrdinalIgnoreCase)) { diagnosticProperties.Add(TextToRemoveKey, text.Substring(0, startingTextSets.Length)); } context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGetsOrSets)); } } else { 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.OrdinalIgnoreCase)) { diagnosticProperties.Add(ExpectedTextKey, startingTextGets); context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGets)); } } } else if (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.OrdinalIgnoreCase)) { diagnosticProperties.Add(ExpectedTextKey, startingTextSets); context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextSets)); } } }
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; } } } if (!(syntax is XmlElementSyntax summaryElement)) { // This is reported by SA1604 or SA1606. 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 = XmlCommentHelper.TryGetFirstTextElementWithContent(summaryElement); string text = textElement is null ? string.Empty : XmlCommentHelper.GetText(textElement, normalizeWhitespace: true).TrimStart(); bool prefixIsGetsOrSets = text.StartsWith(startingTextGetsOrSets, StringComparison.OrdinalIgnoreCase); bool prefixIsGets = !prefixIsGetsOrSets && text.StartsWith(startingTextGets, StringComparison.OrdinalIgnoreCase); bool prefixIsSets = text.StartsWith(startingTextSets, StringComparison.OrdinalIgnoreCase); bool getterVisible, setterVisible; if (getter != null && setter != null) { if (!getter.Modifiers.Any() && !setter.Modifiers.Any()) { // The getter and setter have the same declared accessibility getterVisible = true; setterVisible = true; } else if (getter.Modifiers.Any(SyntaxKind.PrivateKeyword)) { getterVisible = false; setterVisible = true; } else if (setter.Modifiers.Any(SyntaxKind.PrivateKeyword)) { 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) { // 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: 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: 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; } } } } else { if (getter != null || expressionBody != null) { getterVisible = true; setterVisible = false; } else { getterVisible = false; setterVisible = setter != null; } } if (getterVisible) { if (setterVisible) { // Both getter and setter are visible. if (!prefixIsGetsOrSets) { ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextGetsOrSets, unexpectedStartingText1: startingTextGets, unexpectedStartingText2: startingTextSets); } } else if (setter != null) { // Both getter and setter exist, but only getter is visible. if (!prefixIsGets) { if (prefixIsGetsOrSets) { ReportSA1624(context, diagnosticLocation, diagnosticProperties, accessor: "get", expectedStartingText: startingTextGets, startingTextToRemove: startingTextGetsOrSets); } else { ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextGets, unexpectedStartingText1: startingTextSets); } } } else { // Getter exists and is visible. Setter does not exist. if (!prefixIsGets) { ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextGets, unexpectedStartingText1: startingTextSets, unexpectedStartingText2: startingTextGetsOrSets); } } } else if (setterVisible) { if (getter != null) { // Both getter and setter exist, but only setter is visible. if (!prefixIsSets) { if (prefixIsGetsOrSets) { ReportSA1624(context, diagnosticLocation, diagnosticProperties, accessor: "set", expectedStartingText: startingTextSets, startingTextToRemove: startingTextGetsOrSets); } else { ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextSets, unexpectedStartingText1: startingTextGets); } } } else { // Setter exists and is visible. Getter does not exist. if (!prefixIsSets) { ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextSets, unexpectedStartingText1: startingTextGetsOrSets, unexpectedStartingText2: startingTextGets); } } } }