Skip to content

Commit

Permalink
Merge pull request #5107 from Vogel612/refactor/annotation-processing
Browse files Browse the repository at this point in the history
Redesign Annotation Processing
  • Loading branch information
retailcoder committed Sep 7, 2019
2 parents 71b9554 + 4a7b243 commit 225b09b
Show file tree
Hide file tree
Showing 141 changed files with 1,199 additions and 1,344 deletions.
6 changes: 5 additions & 1 deletion Rubberduck.API/VBA/Parser.cs
Expand Up @@ -25,6 +25,7 @@
using Rubberduck.Root;
using Rubberduck.VBEditor.ComManagement.TypeLibs;
using Rubberduck.VBEditor.SourceCodeHandling;
using Rubberduck.Parsing.Annotations;

namespace Rubberduck.API.VBA
{
Expand Down Expand Up @@ -106,6 +107,8 @@ internal Parser(object vbe) : this()
var preprocessorErrorListenerFactory = new PreprocessingParseErrorListenerFactory();
var preprocessorParser = new VBAPreprocessorParser(preprocessorErrorListenerFactory, preprocessorErrorListenerFactory);
var preprocessor = new VBAPreprocessor(preprocessorParser, compilationsArgumentsCache);
// FIXME inject annotations to allow Rubberduck api users to access Annotations from VBA code
var annotationProcessor = new VBAParserAnnotationFactory(new List<IAnnotation>());
var mainParseErrorListenerFactory = new MainParseErrorListenerFactory();
var mainTokenStreamParser = new VBATokenStreamParser(mainParseErrorListenerFactory, mainParseErrorListenerFactory);
var tokenStreamProvider = new SimpleVBAModuleTokenStreamProvider();
Expand Down Expand Up @@ -139,7 +142,8 @@ internal Parser(object vbe) : this()
var moduleParser = new ModuleParser(
codePaneSourceCodeHandler,
attributesSourceCodeHandler,
stringParser);
stringParser,
annotationProcessor);
var parseRunner = new ParseRunner(
_state,
parserStateManager,
Expand Down
Expand Up @@ -49,23 +49,27 @@ public AttributeValueOutOfSyncInspection(RubberduckParserState state)
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
{
var declarationsWithAttributeAnnotations = State.DeclarationFinder.AllUserDeclarations
.Where(declaration => declaration.Annotations.Any(annotation => annotation.AnnotationType.HasFlag(AnnotationType.Attribute)));
.Where(declaration => declaration.Annotations.Any(pta => pta.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>())
foreach (var annotationInstance in declaration.Annotations.Where(pta => pta.Annotation is IAttributeAnnotation))
{
if (HasDifferingAttributeValues(declaration, annotation, out var attributeValues))
// cast is safe given the predicate in the foreach
var annotation = (IAttributeAnnotation)annotationInstance.Annotation;
if (HasDifferingAttributeValues(declaration, annotationInstance, out var attributeValues))
{
var attributeName = annotation.Attribute(annotationInstance);

var description = string.Format(InspectionResults.AttributeValueOutOfSyncInspection,
annotation.Attribute,
attributeName,
string.Join(", ", attributeValues),
annotation.AnnotationType);
annotation.Name);

var result = new DeclarationInspectionResult(this, description, declaration,
new QualifiedContext(declaration.QualifiedModuleName, annotation.Context));
result.Properties.Annotation = annotation;
result.Properties.AttributeName = annotation.Attribute;
new QualifiedContext(declaration.QualifiedModuleName, annotationInstance.Context));
result.Properties.Annotation = annotationInstance;
result.Properties.AttributeName = attributeName;
result.Properties.AttributeValues = attributeValues;

results.Add(result);
Expand All @@ -76,16 +80,22 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
return results;
}

private static bool HasDifferingAttributeValues(Declaration declaration, IAttributeAnnotation annotation, out IReadOnlyList<string> attributeValues)
private static bool HasDifferingAttributeValues(Declaration declaration, IParseTreeAnnotation annotationInstance, out IReadOnlyList<string> attributeValues)
{
if (!(annotationInstance.Annotation is IAttributeAnnotation annotation))
{
attributeValues = new List<string>();
return false;
}
var attribute = annotation.Attribute(annotationInstance);
var attributeNodes = declaration.DeclarationType.HasFlag(DeclarationType.Module)
? declaration.Attributes.AttributeNodesFor(annotation)
: declaration.Attributes.AttributeNodesFor(annotation, declaration.IdentifierName);
? declaration.Attributes.AttributeNodesFor(annotationInstance)
: declaration.Attributes.AttributeNodesFor(annotationInstance, declaration.IdentifierName);

foreach (var attributeNode in attributeNodes)
{
var values = attributeNode.Values;
if (!annotation.AttributeValues.SequenceEqual(values))
if (!annotation.AttributeValues(annotationInstance).SequenceEqual(values))
{
attributeValues = values;
return true;
Expand Down
Expand Up @@ -46,15 +46,15 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
foreach (var declaration in State.AllUserDeclarations)
{
var duplicateAnnotations = declaration.Annotations
.GroupBy(annotation => annotation.AnnotationType)
.Where(group => !group.First().AllowMultiple && group.Count() > 1);
.GroupBy(pta => pta.Annotation)
.Where(group => !group.First().Annotation.AllowMultiple && group.Count() > 1);

issues.AddRange(duplicateAnnotations.Select(duplicate =>
{
var result = new DeclarationInspectionResult(
this, string.Format(InspectionResults.DuplicatedAnnotationInspection, duplicate.Key.ToString()), declaration);
result.Properties.AnnotationType = duplicate.Key;
result.Properties.Annotation = duplicate.Key;
return result;
}));
}
Expand Down
Expand Up @@ -52,7 +52,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
var annotations = State.AllAnnotations;

var unboundAnnotations = UnboundAnnotations(annotations, userDeclarations, identifierReferences)
.Where(annotation => !annotation.AnnotationType.HasFlag(AnnotationType.GeneralAnnotation)
.Where(annotation => !annotation.Annotation.Target.HasFlag(AnnotationTarget.General)
|| annotation.AnnotatedLine == null);
var attributeAnnotationsInDocuments = AttributeAnnotationsInDocuments(userDeclarations);

Expand All @@ -65,7 +65,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
new QualifiedContext(annotation.QualifiedSelection.QualifiedName, annotation.Context)));
}

private static IEnumerable<IAnnotation> UnboundAnnotations(IEnumerable<IAnnotation> annotations, IEnumerable<Declaration> userDeclarations, IEnumerable<IdentifierReference> identifierReferences)
private static IEnumerable<IParseTreeAnnotation> UnboundAnnotations(IEnumerable<IParseTreeAnnotation> annotations, IEnumerable<Declaration> userDeclarations, IEnumerable<IdentifierReference> identifierReferences)
{
var boundAnnotationsSelections = userDeclarations
.SelectMany(declaration => declaration.Annotations)
Expand All @@ -76,11 +76,11 @@ private static IEnumerable<IAnnotation> UnboundAnnotations(IEnumerable<IAnnotati
return annotations.Where(annotation => !boundAnnotationsSelections.Contains(annotation.QualifiedSelection)).ToList();
}

private static IEnumerable<IAnnotation> AttributeAnnotationsInDocuments(IEnumerable<Declaration> userDeclarations)
private static IEnumerable<IParseTreeAnnotation> AttributeAnnotationsInDocuments(IEnumerable<Declaration> userDeclarations)
{
var declarationsInDocuments = userDeclarations
.Where(declaration => declaration.QualifiedModuleName.ComponentType == ComponentType.Document);
return declarationsInDocuments.SelectMany(doc => doc.Annotations).OfType<IAttributeAnnotation>();
return declarationsInDocuments.SelectMany(doc => doc.Annotations).Where(pta => pta.Annotation is IAttributeAnnotation);
}
}
}
Expand Up @@ -48,10 +48,12 @@ public MissingAnnotationArgumentInspection(RubberduckParserState state)

protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
{
// FIXME don't actually use listeners here, iterate the Annotations instead
// FIXME don't maintain a separate list for annotations that require arguments, instead use AnnotationAttribute to store that information
return (from result in Listener.Contexts
let context = (VBAParser.AnnotationContext)result.Context
where context.annotationName().GetText() == AnnotationType.Ignore.ToString()
|| context.annotationName().GetText() == AnnotationType.Folder.ToString()
where context.annotationName().GetText() == "Ignore"
|| context.annotationName().GetText() == "Folder"
where context.annotationArgList() == null
select new QualifiedContextInspectionResult(this,
string.Format(InspectionResults.MissingAnnotationArgumentInspection,
Expand Down
Expand Up @@ -48,21 +48,21 @@ public MissingAttributeInspection(RubberduckParserState state)
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
{
var declarationsWithAttributeAnnotations = State.DeclarationFinder.AllUserDeclarations
.Where(declaration => declaration.Annotations.Any(annotation => annotation.AnnotationType.HasFlag(AnnotationType.Attribute)));
.Where(declaration => declaration.Annotations.Any(pta => pta.Annotation is IAttributeAnnotation));
var results = new List<DeclarationInspectionResult>();
foreach (var declaration in declarationsWithAttributeAnnotations.Where(decl => decl.QualifiedModuleName.ComponentType != ComponentType.Document
&& !decl.IsIgnoringInspectionResultFor(AnnotationName)))
{
foreach(var annotation in declaration.Annotations.OfType<IAttributeAnnotation>())
foreach (var annotationInstance in declaration.Annotations.Where(pta => pta.Annotation is IAttributeAnnotation))
{
if (MissesCorrespondingAttribute(declaration, annotation))
var annotation = (IAttributeAnnotation)annotationInstance.Annotation;
if (MissesCorrespondingAttribute(declaration, annotationInstance))
{
var description = string.Format(InspectionResults.MissingAttributeInspection, declaration.IdentifierName,
annotation.AnnotationType.ToString());
var description = string.Format(InspectionResults.MissingAttributeInspection, declaration.IdentifierName, annotation.Name);

var result = new DeclarationInspectionResult(this, description, declaration,
new QualifiedContext(declaration.QualifiedModuleName, annotation.Context));
result.Properties.Annotation = annotation;
new QualifiedContext(declaration.QualifiedModuleName, annotationInstance.Context));
result.Properties.Annotation = annotationInstance;

results.Add(result);
}
Expand All @@ -72,15 +72,20 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
return results;
}

private static bool MissesCorrespondingAttribute(Declaration declaration, IAttributeAnnotation annotation)
private static bool MissesCorrespondingAttribute(Declaration declaration, IParseTreeAnnotation annotationInstance)
{
if (string.IsNullOrEmpty(annotation.Attribute))
if (!(annotationInstance.Annotation is IAttributeAnnotation annotation))
{
return false;
}
var attribute = annotation.Attribute(annotationInstance);
if (string.IsNullOrEmpty(attribute))
{
return false;
}
return declaration.DeclarationType.HasFlag(DeclarationType.Module)
? !declaration.Attributes.HasAttributeFor(annotation)
: !declaration.Attributes.HasAttributeFor(annotation, declaration.IdentifierName);
? !declaration.Attributes.HasAttributeFor(annotationInstance)
: !declaration.Attributes.HasAttributeFor(annotationInstance, declaration.IdentifierName);
}
}
}
Expand Up @@ -88,16 +88,18 @@ private static bool MissesCorrespondingMemberAnnotation(Declaration declaration,
}

var attributeBaseName = AttributeBaseName(declaration, attribute);

//VB_Ext_Key attributes are special in that identity also depends on the first value, the key.
// VB_Ext_Key attributes are special in that identity also depends on the first value, the key.
if (attributeBaseName == "VB_Ext_Key")
{
return !declaration.Annotations.OfType<IAttributeAnnotation>()
.Any(annotation => annotation.Attribute.Equals("VB_Ext_Key") && attribute.Values[0].Equals(annotation.AttributeValues[0]));
return !declaration.Annotations.Where(pta => pta.Annotation is IAttributeAnnotation)
.Any(pta => {
var annotation = (IAttributeAnnotation)pta.Annotation;
return annotation.Attribute(pta).Equals("VB_Ext_Key") && attribute.Values[0].Equals(annotation.AttributeValues(pta)[0]);
});
}

return !declaration.Annotations.OfType<IAttributeAnnotation>()
.Any(annotation => annotation.Attribute.Equals(attributeBaseName));
return !declaration.Annotations.Where(pta => pta.Annotation is IAttributeAnnotation)
.Any(pta => ((IAttributeAnnotation)pta.Annotation).Attribute(pta).Equals(attributeBaseName));
}

private static string AttributeBaseName(Declaration declaration, AttributeNode attribute)
Expand Down
Expand Up @@ -93,16 +93,18 @@ private static bool MissesCorrespondingModuleAnnotation(Declaration declaration,
{
return false;
}

//VB_Ext_Key attributes are special in that identity also depends on the first value, the key.
if (attribute.Name == "VB_Ext_Key")
{
return !declaration.Annotations.OfType<IAttributeAnnotation>()
.Any(annotation => annotation.Attribute.Equals("VB_Ext_Key") && attribute.Values[0].Equals(annotation.AttributeValues[0]));
return !declaration.Annotations.Where(pta => pta.Annotation is IAttributeAnnotation)
.Any(pta => {
var annotation = (IAttributeAnnotation)pta.Annotation;
return annotation.Attribute(pta).Equals("VB_Ext_Key") && attribute.Values[0].Equals(annotation.AttributeValues(pta)[0]);
});
}

return !declaration.Annotations.OfType<IAttributeAnnotation>()
.Any(annotation => annotation.Attribute.Equals(attribute.Name));
return !declaration.Annotations.Where(pta => pta.Annotation is IAttributeAnnotation)
.Any(pta => ((IAttributeAnnotation)pta.Annotation).Attribute(pta).Equals(attribute.Name));
}
}
}
Expand Up @@ -39,7 +39,7 @@ public ModuleWithoutFolderInspection(RubberduckParserState state)
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
{
var modulesWithoutFolderAnnotation = State.DeclarationFinder.UserDeclarations(Parsing.Symbols.DeclarationType.Module)
.Where(w => w.Annotations.All(a => a.AnnotationType != AnnotationType.Folder))
.Where(w => !w.Annotations.Any(pta => pta.Annotation is FolderAnnotation))
.ToList();

return modulesWithoutFolderAnnotation
Expand Down
Expand Up @@ -58,15 +58,16 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
{
var declarations = State.AllUserDeclarations
.Where(declaration => declaration.DeclarationType.HasFlag(DeclarationType.Member) &&
declaration.Annotations.Any(annotation =>annotation.AnnotationType == AnnotationType.Obsolete));
declaration.Annotations.Any(pta => pta.Annotation is ObsoleteAnnotation));

var issues = new List<IdentifierReferenceInspectionResult>();

foreach (var declaration in declarations)
{
var replacementDocumentation =
((ObsoleteAnnotation) declaration.Annotations.First(annotation =>
annotation.AnnotationType == AnnotationType.Obsolete)).ReplacementDocumentation;
var replacementDocumentation = declaration.Annotations
.First(pta => pta.Annotation is ObsoleteAnnotation)
.AnnotationArguments
.FirstOrDefault() ?? string.Empty;

issues.AddRange(declaration.References.Select(reference =>
new IdentifierReferenceInspectionResult(this,
Expand Down

0 comments on commit 225b09b

Please sign in to comment.