/
AttributeValueOutOfSyncInspection.cs
98 lines (91 loc) · 4.01 KB
/
AttributeValueOutOfSyncInspection.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
using System.Collections.Generic;
using System.Linq;
using Rubberduck.Inspections.Abstract;
using Rubberduck.Inspections.Results;
using Rubberduck.Parsing;
using Rubberduck.Parsing.Annotations;
using Rubberduck.Parsing.Inspections;
using Rubberduck.Parsing.Inspections.Abstract;
using Rubberduck.Parsing.Symbols;
using Rubberduck.Parsing.VBA;
using Rubberduck.Resources.Inspections;
using Rubberduck.VBEditor.SafeComWrappers;
namespace Rubberduck.Inspections.Concrete
{
/// <summary>
/// Indicates that the value of a hidden VB attribute is out of sync with the corresponding Rubberduck annotation comment.
/// </summary>
/// <why>
/// Keeping Rubberduck annotation comments in sync with the hidden VB attribute values, surfaces these hidden attributes in the VBE code panes;
/// Rubberduck can rewrite the attributes to match the corresponding annotation comment.
/// </why>
/// <example hasResults="true">
/// <![CDATA[
/// '@Description("foo")
/// Public Sub DoSomething()
/// Attribute VB_Description = "bar"
/// ' ...
/// End Sub
/// ]]>
/// </example>
/// <example hasResults="false">
/// <![CDATA[
/// '@Description("foo")
/// Public Sub DoSomething()
/// Attribute VB_Description = "foo"
/// ' ...
/// End Sub
/// ]]>
/// </example>
[CannotAnnotate]
public sealed class AttributeValueOutOfSyncInspection : InspectionBase
{
public AttributeValueOutOfSyncInspection(RubberduckParserState state)
:base(state)
{
}
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
{
var declarationsWithAttributeAnnotations = State.DeclarationFinder.AllUserDeclarations
.Where(declaration => declaration.Annotations.Any(annotation => annotation is IAttributeAnnotation));
var results = new List<DeclarationInspectionResult>();
foreach (var declaration in declarationsWithAttributeAnnotations.Where(decl => decl.QualifiedModuleName.ComponentType != ComponentType.Document))
{
foreach (var annotation in declaration.Annotations.OfType<IAttributeAnnotation>())
{
if (HasDifferingAttributeValues(declaration, annotation, out var attributeValues))
{
var description = string.Format(InspectionResults.AttributeValueOutOfSyncInspection,
annotation.Attribute,
string.Join(", ", attributeValues),
annotation.AnnotationType);
var result = new DeclarationInspectionResult(this, description, declaration,
new QualifiedContext(declaration.QualifiedModuleName, annotation.Context));
result.Properties.Annotation = annotation;
result.Properties.AttributeName = annotation.Attribute;
result.Properties.AttributeValues = attributeValues;
results.Add(result);
}
}
}
return results;
}
private static bool HasDifferingAttributeValues(Declaration declaration, IAttributeAnnotation annotation, out IReadOnlyList<string> attributeValues)
{
var attributeNodes = declaration.DeclarationType.HasFlag(DeclarationType.Module)
? declaration.Attributes.AttributeNodesFor(annotation)
: declaration.Attributes.AttributeNodesFor(annotation, declaration.IdentifierName);
foreach (var attributeNode in attributeNodes)
{
var values = attributeNode.Values;
if (!annotation.AttributeValues.SequenceEqual(values))
{
attributeValues = values;
return true;
}
}
attributeValues = new List<string>();
return false;
}
}
}