Skip to content

Commit fea4367

Browse files
authored
Merge pull request #4669 from MDoerner/ReimplementMissingAnnotationInspections
Reimplement missing annotation inspections
2 parents 6b306ba + 5f729b2 commit fea4367

32 files changed

+1328
-174
lines changed

Rubberduck.CodeAnalysis/Inspections/Concrete/AttributeValueOutOfSyncInspection.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Rubberduck.Parsing.Symbols;
1010
using Rubberduck.Parsing.VBA;
1111
using Rubberduck.Resources.Inspections;
12+
using Rubberduck.VBEditor.SafeComWrappers;
1213

1314
namespace Rubberduck.Inspections.Concrete
1415
{
@@ -25,7 +26,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
2526
var declarationsWithAttributeAnnotations = State.DeclarationFinder.AllUserDeclarations
2627
.Where(declaration => declaration.Annotations.Any(annotation => annotation.AnnotationType.HasFlag(AnnotationType.Attribute)));
2728
var results = new List<DeclarationInspectionResult>();
28-
foreach (var declaration in declarationsWithAttributeAnnotations)
29+
foreach (var declaration in declarationsWithAttributeAnnotations.Where(decl => decl.QualifiedModuleName.ComponentType != ComponentType.Document))
2930
{
3031
foreach (var annotation in declaration.Annotations.OfType<IAttributeAnnotation>())
3132
{
@@ -39,6 +40,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
3940
var result = new DeclarationInspectionResult(this, description, declaration,
4041
new QualifiedContext(declaration.QualifiedModuleName, annotation.Context));
4142
result.Properties.Annotation = annotation;
43+
result.Properties.AttributeName = annotation.Attribute;
4244
result.Properties.AttributeValues = attributeValues;
4345

4446
results.Add(result);

Rubberduck.CodeAnalysis/Inspections/Concrete/IllegalAnnotationInspection.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
using Rubberduck.Parsing.Symbols;
99
using Rubberduck.Resources.Inspections;
1010
using Rubberduck.Parsing.VBA;
11+
using Rubberduck.Parsing.VBA.DeclarationCaching;
1112
using Rubberduck.Parsing.VBA.Extensions;
13+
using Rubberduck.VBEditor.SafeComWrappers;
1214

1315
namespace Rubberduck.Inspections.Concrete
1416
{
@@ -24,9 +26,12 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
2426
var identifierReferences = State.DeclarationFinder.AllIdentifierReferences().ToList();
2527
var annotations = State.AllAnnotations;
2628

27-
var illegalAnnotations = UnboundAnnotations(annotations, userDeclarations, identifierReferences)
29+
var unboundAnnotations = UnboundAnnotations(annotations, userDeclarations, identifierReferences)
2830
.Where(annotation => !annotation.AnnotationType.HasFlag(AnnotationType.GeneralAnnotation)
29-
|| annotation.AnnotatedLine == null);
31+
|| annotation.AnnotatedLine == null);
32+
var attributeAnnotationsInDocuments = AttributeAnnotationsInDocuments(userDeclarations);
33+
34+
var illegalAnnotations = unboundAnnotations.Concat(attributeAnnotationsInDocuments).ToHashSet();
3035

3136
return illegalAnnotations.Select(annotation =>
3237
new QualifiedContextInspectionResult(
@@ -35,7 +40,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
3540
new QualifiedContext(annotation.QualifiedSelection.QualifiedName, annotation.Context)));
3641
}
3742

38-
private static ICollection<IAnnotation> UnboundAnnotations(IEnumerable<IAnnotation> annotations, IEnumerable<Declaration> userDeclarations, IEnumerable<IdentifierReference> identifierReferences)
43+
private static IEnumerable<IAnnotation> UnboundAnnotations(IEnumerable<IAnnotation> annotations, IEnumerable<Declaration> userDeclarations, IEnumerable<IdentifierReference> identifierReferences)
3944
{
4045
var boundAnnotationsSelections = userDeclarations
4146
.SelectMany(declaration => declaration.Annotations)
@@ -45,5 +50,12 @@ private static ICollection<IAnnotation> UnboundAnnotations(IEnumerable<IAnnotati
4550

4651
return annotations.Where(annotation => !boundAnnotationsSelections.Contains(annotation.QualifiedSelection)).ToList();
4752
}
53+
54+
private static IEnumerable<IAnnotation> AttributeAnnotationsInDocuments(IEnumerable<Declaration> userDeclarations)
55+
{
56+
var declarationsInDocuments = userDeclarations
57+
.Where(declaration => declaration.QualifiedModuleName.ComponentType == ComponentType.Document);
58+
return declarationsInDocuments.SelectMany(doc => doc.Annotations).OfType<IAttributeAnnotation>();
59+
}
4860
}
4961
}

Rubberduck.CodeAnalysis/Inspections/Concrete/MissingAttributeInspection.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Rubberduck.Parsing.Symbols;
1010
using Rubberduck.Parsing.VBA;
1111
using Rubberduck.Resources.Inspections;
12+
using Rubberduck.VBEditor.SafeComWrappers;
1213

1314
namespace Rubberduck.Inspections.Concrete
1415
{
@@ -24,7 +25,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
2425
var declarationsWithAttributeAnnotations = State.DeclarationFinder.AllUserDeclarations
2526
.Where(declaration => declaration.Annotations.Any(annotation => annotation.AnnotationType.HasFlag(AnnotationType.Attribute)));
2627
var results = new List<DeclarationInspectionResult>();
27-
foreach (var declaration in declarationsWithAttributeAnnotations)
28+
foreach (var declaration in declarationsWithAttributeAnnotations.Where(decl => decl.QualifiedModuleName.ComponentType != ComponentType.Document))
2829
{
2930
foreach(var annotation in declaration.Annotations.OfType<IAttributeAnnotation>())
3031
{
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Rubberduck.Inspections.Abstract;
4+
using Rubberduck.Inspections.Results;
5+
using Rubberduck.Parsing;
6+
using Rubberduck.Parsing.Annotations;
7+
using Rubberduck.Parsing.Inspections.Abstract;
8+
using Rubberduck.Parsing.Symbols;
9+
using Rubberduck.Parsing.VBA;
10+
using Rubberduck.Resources.Inspections;
11+
using Rubberduck.VBEditor.SafeComWrappers;
12+
13+
namespace Rubberduck.Inspections.Concrete
14+
{
15+
public sealed class MissingMemberAnnotationInspection : InspectionBase
16+
{
17+
public MissingMemberAnnotationInspection(RubberduckParserState state)
18+
:base(state)
19+
{}
20+
21+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
22+
{
23+
var memberDeclarationsWithAttributes = State.DeclarationFinder.AllUserDeclarations
24+
.Where(decl => !decl.DeclarationType.HasFlag(DeclarationType.Module)
25+
&& decl.Attributes.Any());
26+
27+
var declarationsToInspect = memberDeclarationsWithAttributes
28+
.Where(decl => decl.QualifiedModuleName.ComponentType != ComponentType.Document
29+
&& !IsIgnoringInspectionResultFor(decl, AnnotationName));
30+
31+
var results = new List<DeclarationInspectionResult>();
32+
foreach (var declaration in declarationsToInspect)
33+
{
34+
foreach (var attribute in declaration.Attributes)
35+
{
36+
if (MissesCorrespondingMemberAnnotation(declaration, attribute))
37+
{
38+
var attributeBaseName = AttributeBaseName(declaration, attribute);
39+
40+
var description = string.Format(InspectionResults.MissingMemberAnnotationInspection,
41+
declaration.IdentifierName,
42+
attributeBaseName,
43+
string.Join(", ", attribute.Values));
44+
45+
var result = new DeclarationInspectionResult(this, description, declaration,
46+
new QualifiedContext(declaration.QualifiedModuleName, declaration.Context));
47+
result.Properties.AttributeName = attributeBaseName;
48+
result.Properties.AttributeValues = attribute.Values;
49+
50+
results.Add(result);
51+
}
52+
}
53+
}
54+
55+
return results;
56+
}
57+
58+
private static bool MissesCorrespondingMemberAnnotation(Declaration declaration, AttributeNode attribute)
59+
{
60+
if (string.IsNullOrEmpty(attribute.Name) || declaration.DeclarationType.HasFlag(DeclarationType.Module))
61+
{
62+
return false;
63+
}
64+
65+
var attributeBaseName = AttributeBaseName(declaration, attribute);
66+
67+
//VB_Ext_Key attributes are special in that identity also depends on the first value, the key.
68+
if (attributeBaseName == "VB_Ext_Key")
69+
{
70+
return !declaration.Annotations.OfType<IAttributeAnnotation>()
71+
.Any(annotation => annotation.Attribute.Equals("VB_Ext_Key") && attribute.Values[0].Equals(annotation.AttributeValues[0]));
72+
}
73+
74+
return !declaration.Annotations.OfType<IAttributeAnnotation>()
75+
.Any(annotation => annotation.Attribute.Equals(attributeBaseName));
76+
}
77+
78+
private static string AttributeBaseName(Declaration declaration, AttributeNode attribute)
79+
{
80+
var attributeName = attribute.Name;
81+
return attributeName.StartsWith($"{declaration.IdentifierName}.")
82+
? attributeName.Substring(declaration.IdentifierName.Length + 1)
83+
: attributeName;
84+
}
85+
}
86+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Rubberduck.Inspections.Abstract;
4+
using Rubberduck.Inspections.Results;
5+
using Rubberduck.Parsing;
6+
using Rubberduck.Parsing.Annotations;
7+
using Rubberduck.Parsing.Grammar;
8+
using Rubberduck.Parsing.Inspections.Abstract;
9+
using Rubberduck.Parsing.Symbols;
10+
using Rubberduck.Parsing.VBA;
11+
using Rubberduck.Resources.Inspections;
12+
using Rubberduck.VBEditor.SafeComWrappers;
13+
14+
namespace Rubberduck.Inspections.Concrete
15+
{
16+
public sealed class MissingModuleAnnotationInspection : InspectionBase
17+
{
18+
public MissingModuleAnnotationInspection(RubberduckParserState state)
19+
:base(state)
20+
{}
21+
22+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
23+
{
24+
var moduleDeclarationsWithAttributes = State.DeclarationFinder
25+
.UserDeclarations(DeclarationType.Module)
26+
.Where(decl => decl.Attributes.Any());
27+
28+
var declarationsToInspect = moduleDeclarationsWithAttributes
29+
.Where(decl => decl.QualifiedModuleName.ComponentType != ComponentType.Document
30+
&& !IsIgnoringInspectionResultFor(decl, AnnotationName));
31+
32+
var results = new List<DeclarationInspectionResult>();
33+
foreach (var declaration in declarationsToInspect)
34+
{
35+
foreach (var attribute in declaration.Attributes)
36+
{
37+
if (IsDefaultAttribute(declaration, attribute))
38+
{
39+
continue;
40+
}
41+
42+
if (MissesCorrespondingModuleAnnotation(declaration, attribute))
43+
{
44+
var description = string.Format(InspectionResults.MissingMemberAnnotationInspection,
45+
declaration.IdentifierName,
46+
attribute.Name,
47+
string.Join(", ", attribute.Values));
48+
49+
var result = new DeclarationInspectionResult(this, description, declaration,
50+
new QualifiedContext(declaration.QualifiedModuleName, declaration.Context));
51+
result.Properties.AttributeName = attribute.Name;
52+
result.Properties.AttributeValues = attribute.Values;
53+
54+
results.Add(result);
55+
}
56+
}
57+
}
58+
59+
return results;
60+
}
61+
62+
private static bool IsDefaultAttribute(Declaration declaration, AttributeNode attribute)
63+
{
64+
switch (attribute.Name)
65+
{
66+
case "VB_Name":
67+
return true;
68+
case "VB_GlobalNameSpace":
69+
return declaration.DeclarationType.HasFlag(DeclarationType.ClassModule)
70+
&& attribute.Values[0].Equals(Tokens.False);
71+
case "VB_Exposed":
72+
return declaration.DeclarationType.HasFlag(DeclarationType.ClassModule)
73+
&& attribute.Values[0].Equals(Tokens.False);
74+
case "VB_Creatable":
75+
return declaration.DeclarationType.HasFlag(DeclarationType.ClassModule)
76+
&& attribute.Values[0].Equals(Tokens.False);
77+
case "VB_PredeclaredId":
78+
return (declaration.QualifiedModuleName.ComponentType == ComponentType.ClassModule
79+
&& attribute.Values[0].Equals(Tokens.False))
80+
|| (declaration.QualifiedModuleName.ComponentType == ComponentType.UserForm
81+
&& attribute.Values[0].Equals(Tokens.True));
82+
default:
83+
return false;
84+
}
85+
}
86+
87+
private static bool MissesCorrespondingModuleAnnotation(Declaration declaration, AttributeNode attribute)
88+
{
89+
if (string.IsNullOrEmpty(attribute.Name) || !declaration.DeclarationType.HasFlag(DeclarationType.Module))
90+
{
91+
return false;
92+
}
93+
94+
//VB_Ext_Key attributes are special in that identity also depends on the first value, the key.
95+
if (attribute.Name == "VB_Ext_Key")
96+
{
97+
return !declaration.Annotations.OfType<IAttributeAnnotation>()
98+
.Any(annotation => annotation.Attribute.Equals("VB_Ext_Key") && attribute.Values[0].Equals(annotation.AttributeValues[0]));
99+
}
100+
101+
return !declaration.Annotations.OfType<IAttributeAnnotation>()
102+
.Any(annotation => annotation.Attribute.Equals(attribute.Name));
103+
}
104+
}
105+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Collections.Generic;
2+
using Rubberduck.Inspections.Abstract;
3+
using Rubberduck.Inspections.Concrete;
4+
using Rubberduck.Parsing.Inspections.Abstract;
5+
using Rubberduck.Parsing.Rewriter;
6+
using Rubberduck.Parsing.Symbols;
7+
using Rubberduck.Parsing.VBA;
8+
using Rubberduck.Parsing.VBA.Parsing;
9+
10+
namespace Rubberduck.Inspections.QuickFixes
11+
{
12+
public class RemoveAttributeQuickFix : QuickFixBase
13+
{
14+
private readonly IAttributesUpdater _attributesUpdater;
15+
16+
public RemoveAttributeQuickFix(IAttributesUpdater attributesUpdater)
17+
:base(typeof(AttributeValueOutOfSyncInspection))
18+
{
19+
_attributesUpdater = attributesUpdater;
20+
}
21+
22+
public override void Fix(IInspectionResult result, IRewriteSession rewriteSession)
23+
{
24+
var declaration = result.Target;
25+
string attributeBaseName = result.Properties.AttributeName;
26+
IReadOnlyList<string> attributeValues = result.Properties.AttributeValues;
27+
28+
var attributeName = declaration.DeclarationType.HasFlag(DeclarationType.Module)
29+
? attributeBaseName
30+
: $"{declaration.IdentifierName}.{attributeBaseName}";
31+
32+
_attributesUpdater.RemoveAttribute(rewriteSession, declaration, attributeName, attributeValues);
33+
}
34+
35+
public override string Description(IInspectionResult result) => Resources.Inspections.QuickFixes.RemoveAttributeQuickFix;
36+
37+
public override CodeKind TargetCodeKind => CodeKind.AttributesCode;
38+
39+
public override bool CanFixInProcedure => false;
40+
public override bool CanFixInModule => false;
41+
public override bool CanFixInProject => false;
42+
}
43+
}

0 commit comments

Comments
 (0)