Skip to content

Commit

Permalink
Add the AttributeValueOutOfSyncInspection
Browse files Browse the repository at this point in the history
This inspection finds instances where a Rubberduck attribute annotation is used, the attribute exists, but the values do not agree.

This commit also changes the default severity of the realated MissingAttributeInspection to Warning.
  • Loading branch information
MDoerner committed Dec 17, 2018
1 parent 4a6976f commit 041c36a
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 22 deletions.
@@ -0,0 +1,69 @@
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;

namespace Rubberduck.Inspections.Concrete
{
[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.AnnotationType.HasFlag(AnnotationType.Attribute)));
var results = new List<DeclarationInspectionResult>();
foreach (var declaration in declarationsWithAttributeAnnotations)
{
foreach (var annotation in declaration.Annotations.OfType<IAttributeAnnotation>())
{
if (HasDifferingAttributeValues(declaration, annotation, out var attributeValues))
{
var description = string.Format(InspectionResults.AttributeValueOutOfSyncInspection, declaration.IdentifierName,
annotation.Attribute, string.Join(", ", annotation.AttributeValues), string.Join(", ", attributeValues));

var result = new DeclarationInspectionResult(this, description, declaration,
new QualifiedContext(declaration.QualifiedModuleName, annotation.Context));
result.Properties.Annotation = annotation;
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).ToList()
: declaration.Attributes.AttributeNodesFor(annotation, declaration.IdentifierName).ToList();

foreach (var attributeNode in attributeNodes)
{
var values = attributeNode.Values;
if (!annotation.AttributeValues.SequenceEqual(values))
{
attributeValues = values;
return true;
}
}
attributeValues = new List<string>();
return false;
}
}
}
4 changes: 3 additions & 1 deletion Rubberduck.Core/app.config
Expand Up @@ -39,7 +39,9 @@
InspectionType="RubberduckOpportunities" />
<CodeInspection Name="RedundantByRefModifierInspection" Severity="DoNotShow"
InspectionType="CodeQualityIssues" />
<CodeInspection Name="MissingAttributeInspection" Severity="DoNotShow"
<CodeInspection Name="MissingAttributeInspection" Severity="Warning"
InspectionType="RubberduckOpportunities" />
<CodeInspection Name="AttributeOutOfSyncInspection" Severity="Warning"
InspectionType="RubberduckOpportunities" />
<CodeInspection Name="MissingAnnotationArgumentInspection" Severity="Error"
InspectionType="CodeQualityIssues" />
Expand Down
26 changes: 8 additions & 18 deletions Rubberduck.Parsing/Symbols/Attributes.cs
Expand Up @@ -76,10 +76,15 @@ public override int GetHashCode()
public class Attributes : HashSet<AttributeNode>
{
public bool HasAttributeFor(IAttributeAnnotation annotation, string memberName = null)
{
return AttributeNodesFor(annotation, memberName).Any();
}

public IEnumerable<AttributeNode> AttributeNodesFor(IAttributeAnnotation annotation, string memberName = null)
{
if (!annotation.AnnotationType.HasFlag(AnnotationType.Attribute))
{
return false;
return Enumerable.Empty<AttributeNode>();
}

var attributeName = memberName != null
Expand All @@ -89,26 +94,11 @@ public bool HasAttributeFor(IAttributeAnnotation annotation, string memberName =
//VB_Ext_Key annotation depend on the defined key for identity.
if (annotation.Attribute.Equals("VB_Ext_Key", StringComparison.OrdinalIgnoreCase))
{
return this.Any(a => a.Name.Equals(attributeName, StringComparison.OrdinalIgnoreCase)
return this.Where(a => a.Name.Equals(attributeName, StringComparison.OrdinalIgnoreCase)
&& a.Values[0] == annotation.AttributeValues[0]);
}

return this.Any(a => a.Name.Equals(attributeName, StringComparison.OrdinalIgnoreCase));
}

public bool HasAttributeWithMatchingValueFor(IAttributeAnnotation annotation, string memberName = null)
{
if (!annotation.AnnotationType.HasFlag(AnnotationType.Attribute))
{
return false;
}

var attributeName = memberName != null
? $"{memberName}.{annotation.Attribute}"
: annotation.Attribute;

return this.Any(a => a.Name.Equals(attributeName, StringComparison.OrdinalIgnoreCase)
&& a.Values.SequenceEqual(annotation.AttributeValues));
return this.Where(a => a.Name.Equals(attributeName, StringComparison.OrdinalIgnoreCase));
}

/// <summary>
Expand Down
12 changes: 11 additions & 1 deletion Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Rubberduck.Resources/Inspections/InspectionInfo.de.resx
Expand Up @@ -358,4 +358,7 @@ Falls der Parameter 'null' sein kann, bitte dieses Auftreten ignorieren. 'null'
<data name="ExcelUdfNameIsValidCellReferenceInspection" xml:space="preserve">
<value>Funktionen, die für EXCEL als benutzerdefinierte Funktionen sichtbar sind, werden einen '#REF!'-Fehler erzeugen, wenn sie in einem Arbeitsblatt verwendet werden, in dem ihr Name dem einer korrekten Zellreferenz entspricht. Wenn die Funktion als benutzerdefinierte Funktion gedacht ist, muss sie umbenannt werden. Wenn die Funktion nicht als benutzerdefinierte Funktion gedacht ist, sollte sie 'Private' deklariert oder aus einem Standardmodul herausgenommen werden.</value>
</data>
<data name="AttributeValueOutOfSyncInspection" xml:space="preserve">
<value>Eine Rubberduck-Annotation wurde für ein Modul oder Element festgelegt, aber das zugehörige Attribut hat einen anderen Wert. Die Modul-Attribute und Annotationen sollten synchronisiert werden.</value>
</data>
</root>
3 changes: 3 additions & 0 deletions Rubberduck.Resources/Inspections/InspectionInfo.resx
Expand Up @@ -358,4 +358,7 @@ If the parameter can be null, ignore this inspection result; passing a null valu
<data name="ExcelUdfNameIsValidCellReferenceInspection" xml:space="preserve">
<value>Functions that are visible to Excel as User-Defined Functions will return a '#REF!' error when used on a Worksheet if they match the name of a valid cell reference. If the function is intended to be used as a UDF, it must be renamed. If the function is not intended to be used as a UDF, it should be scoped as 'Private' or moved out of a standard Module.</value>
</data>
<data name="AttributeValueOutOfSyncInspection" xml:space="preserve">
<value>A Rubberduck annotation is specified for a module or member, but the corresponding attribute has a different value. Module attributes and annotations need to be synchronized.</value>
</data>
</root>
12 changes: 11 additions & 1 deletion Rubberduck.Resources/Inspections/InspectionNames.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Rubberduck.Resources/Inspections/InspectionNames.de.resx
Expand Up @@ -357,4 +357,7 @@
<data name="ExcelUdfNameIsValidCellReferenceInspection" xml:space="preserve">
<value>Funktion wird von EXCEL-Zellreferenz verdeckt</value>
</data>
<data name="AttributeValueOutOfSyncInspection" xml:space="preserve">
<value>Wert stimmt nicht überein zwischen Attribut und Annotation</value>
</data>
</root>
3 changes: 3 additions & 0 deletions Rubberduck.Resources/Inspections/InspectionNames.resx
Expand Up @@ -357,4 +357,7 @@
<data name="ExcelUdfNameIsValidCellReferenceInspection" xml:space="preserve">
<value>Function is hidden by Excel cell reference</value>
</data>
<data name="AttributeValueOutOfSyncInspection" xml:space="preserve">
<value>Value does not match between attribute and annotation</value>
</data>
</root>
12 changes: 11 additions & 1 deletion Rubberduck.Resources/Inspections/InspectionResults.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Rubberduck.Resources/Inspections/InspectionResults.de.resx
Expand Up @@ -374,4 +374,7 @@
<data name="ExcelUdfNameIsValidCellReferenceInspection" xml:space="preserve">
<value>'{0}' wird von einem EXCEL-Zellverweis verdeckt.</value>
</data>
<data name="AttributeValueOutOfSyncInspection" xml:space="preserve">
<value>Die Wert(e) des {0}-Attributs ({1}) passen nicht zur {2}-Annotation.</value>
</data>
</root>
4 changes: 4 additions & 0 deletions Rubberduck.Resources/Inspections/InspectionResults.resx
Expand Up @@ -389,4 +389,8 @@
<value>'{0}' is hidden by a valid Excel cell reference.</value>
<comment>{0} Function name</comment>
</data>
<data name="AttributeValueOutOfSyncInspection" xml:space="preserve">
<value>The attribute value(s) for attribute {0} ({1}) are out of sync with the {2} annotation. </value>
<comment>{0} attribute name, {1} attribute values, {2} annotation name</comment>
</data>
</root>

0 comments on commit 041c36a

Please sign in to comment.