Skip to content

Commit 873b60b

Browse files
committed
Add DocumentValueFromSummary analyzer and code fix
Fixes #6
1 parent efb505e commit 873b60b

4 files changed

Lines changed: 435 additions & 0 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
namespace OpenStackNetAnalyzers
2+
{
3+
using System;
4+
using System.Collections.Immutable;
5+
using System.Linq;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.CSharp;
8+
using Microsoft.CodeAnalysis.CSharp.Syntax;
9+
using Microsoft.CodeAnalysis.Diagnostics;
10+
11+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
12+
public class DocumentValueFromSummaryAnalyzer : DiagnosticAnalyzer
13+
{
14+
public const string DiagnosticId = "DocumentValueFromSummary";
15+
internal const string Title = "JSON property values should be documented";
16+
internal const string MessageFormat = "JSON property values should be documented";
17+
internal const string Category = "OpenStack.Documentation";
18+
internal const string Description = "JSON property values should be documented";
19+
20+
private static DiagnosticDescriptor Descriptor =
21+
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
22+
23+
private static readonly ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics =
24+
ImmutableArray.Create(Descriptor);
25+
26+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
27+
{
28+
get
29+
{
30+
return _supportedDiagnostics;
31+
}
32+
}
33+
34+
public override void Initialize(AnalysisContext context)
35+
{
36+
context.RegisterSyntaxNodeAction(HandlePropertyDeclaration, SyntaxKind.PropertyDeclaration);
37+
}
38+
39+
private void HandlePropertyDeclaration(SyntaxNodeAnalysisContext context)
40+
{
41+
PropertyDeclarationSyntax syntax = (PropertyDeclarationSyntax)context.Node;
42+
if (syntax.Identifier.IsMissing)
43+
return;
44+
45+
if (!syntax.Modifiers.Any(SyntaxKind.PublicKeyword) && !syntax.Modifiers.Any(SyntaxKind.ProtectedKeyword))
46+
return;
47+
48+
SemanticModel semanticModel = context.SemanticModel;
49+
IPropertySymbol propertySymbol = semanticModel.GetDeclaredSymbol(syntax, context.CancellationToken);
50+
if (propertySymbol == null)
51+
return;
52+
53+
INamedTypeSymbol declaringType = propertySymbol.ContainingType;
54+
if (!IsExtensibleJsonObject(declaringType))
55+
return;
56+
57+
DocumentationCommentTriviaSyntax documentationTriviaSyntax = GetDocumentationCommentTriviaSyntax(syntax);
58+
if (documentationTriviaSyntax == null)
59+
return;
60+
61+
XmlNodeSyntax valueNode = GetXmlElement(documentationTriviaSyntax.Content, "value");
62+
if (valueNode != null)
63+
return;
64+
65+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, syntax.Identifier.GetLocation()));
66+
}
67+
68+
private bool IsExtensibleJsonObject(INamedTypeSymbol symbol)
69+
{
70+
while (symbol != null && symbol.SpecialType != SpecialType.System_Object)
71+
{
72+
if (string.Equals("global::OpenStack.ObjectModel.ExtensibleJsonObject", symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), StringComparison.Ordinal))
73+
return true;
74+
75+
symbol = symbol.BaseType;
76+
}
77+
78+
return false;
79+
}
80+
81+
internal static DocumentationCommentTriviaSyntax GetDocumentationCommentTriviaSyntax(SyntaxNode node)
82+
{
83+
if (node == null)
84+
return null;
85+
86+
return node
87+
.GetLeadingTrivia()
88+
.Select(i => i.GetStructure())
89+
.OfType<DocumentationCommentTriviaSyntax>()
90+
.FirstOrDefault();
91+
}
92+
93+
internal static XmlNodeSyntax GetXmlElement(SyntaxList<XmlNodeSyntax> content, string elementName)
94+
{
95+
foreach (XmlNodeSyntax syntax in content)
96+
{
97+
XmlEmptyElementSyntax emptyElement = syntax as XmlEmptyElementSyntax;
98+
if (emptyElement != null)
99+
{
100+
if (string.Equals(elementName, emptyElement.Name.ToString(), StringComparison.Ordinal))
101+
return emptyElement;
102+
103+
continue;
104+
}
105+
106+
XmlElementSyntax elementSyntax = syntax as XmlElementSyntax;
107+
if (elementSyntax != null)
108+
{
109+
if (string.Equals(elementName, elementSyntax.StartTag?.Name?.ToString(), StringComparison.Ordinal))
110+
return elementSyntax;
111+
112+
continue;
113+
}
114+
}
115+
116+
return null;
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)