protected override void VisitResolvedAttribute( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { VisitAttributeValue(element, attribute, resolvedElement, resolvedAttribute); base.VisitResolvedAttribute(element, attribute, resolvedElement, resolvedAttribute); }
protected override void VisitValue( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ValueInfo info, string value, int offset) { if (!IsTargetsFile && !IsPropsFile) { if (info.DefaultValue != null && string.Equals(info.DefaultValue, value)) { Document.Diagnostics.Add(CoreDiagnostics.HasDefaultValue, new TextSpan(offset, value.Length), DescriptionFormatter.GetKindNoun(info), info.Name, info.DefaultValue); } } //NOTE: doing this here means we can't check for deprecated constructs that don't have values, but there aren't any yet CheckDeprecated(info, (INamedXObject)attribute ?? element); // we skip calling base, and instead parse the expression with more options enabled // so that we can warn if the user is doing something likely invalid var kind = MSBuildCompletionExtensions.InferValueKindIfUnknown(info); var options = kind.GetExpressionOptions() | ExpressionOptions.ItemsMetadataAndLists; var node = ExpressionParser.Parse(value, options, offset); VisitValueExpression(element, attribute, resolvedElement, resolvedAttribute, info, kind, node); }
protected override void VisitValue( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ITypedSymbol valueDescriptor, string value, int offset) { if (!IsTargetsFile && !IsPropsFile && valueDescriptor is IHasDefaultValue hasDefault) { if (hasDefault.DefaultValue != null && string.Equals(hasDefault.DefaultValue, value, StringComparison.OrdinalIgnoreCase)) { Document.Diagnostics.Add( CoreDiagnostics.HasDefaultValue, attribute?.Span ?? element.OuterSpan, ImmutableDictionary <string, object> .Empty.Add("Info", valueDescriptor), DescriptionFormatter.GetKindNoun(valueDescriptor), valueDescriptor.Name, hasDefault.DefaultValue); } } if (valueDescriptor is IDeprecatable deprecatable) { CheckDeprecated(deprecatable, (INamedXObject)attribute ?? element); } // we skip calling base, and instead parse the expression with more options enabled // so that we can warn if the user is doing something likely invalid var kind = MSBuildCompletionExtensions.InferValueKindIfUnknown(valueDescriptor); var options = kind.GetExpressionOptions() | ExpressionOptions.ItemsMetadataAndLists; var node = ExpressionParser.Parse(value, options, offset); VisitValueExpression(element, attribute, resolvedElement, resolvedAttribute, valueDescriptor, kind, node); }
protected virtual void VisitResolvedAttribute( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { if (attribute.Value != null) { VisitAttributeValue(element, attribute, resolvedElement, resolvedAttribute, attribute.Value, attribute.ValueOffset); } }
internal AttributeDiagnosticContext( MSBuildAnalysisSession session, XElement element, XAttribute attribute, MSBuildElementSyntax elementSyntax, MSBuildAttributeSyntax attributeSyntax) { this.session = session; Element = element; Attribute = attribute; ElementSyntax = elementSyntax; AttributeSyntax = attributeSyntax; }
protected override void VisitResolvedAttribute( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { if (!attribute.Span.Contains(offset)) { return; } rr.AttributeSyntax = resolvedAttribute = Document.GetSchemas().SpecializeAttribute(resolvedAttribute, element.Name.Name); bool inName = attribute.NameSpan.Contains(offset); if (inName) { rr.ReferenceOffset = attribute.Span.Start; rr.ReferenceLength = attribute.Name.Name.Length; switch (resolvedAttribute.AbstractKind) { case MSBuildSyntaxKind.Metadata: rr.ReferenceKind = MSBuildReferenceKind.Metadata; rr.Reference = (element.Name.Name, attribute.Name.Name); break; case MSBuildSyntaxKind.Parameter: rr.ReferenceKind = MSBuildReferenceKind.TaskParameter; rr.Reference = (element.Name.Name, attribute.Name.Name); break; default: if (!resolvedAttribute.IsAbstract) { rr.ReferenceKind = MSBuildReferenceKind.Keyword; rr.Reference = resolvedAttribute; } break; } return; } base.VisitResolvedAttribute(element, attribute, resolvedElement, resolvedAttribute); }
void VisitAttributeValue( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { if (string.IsNullOrWhiteSpace(attribute.Value)) { return; } var info = Document.GetSchemas().GetAttributeInfo(resolvedAttribute, element.Name.Name, attribute.Name.Name); if (info == null) { return; } VisitValue( element, attribute, resolvedElement, resolvedAttribute, info, attribute.Value, attribute.ValueOffset); }
protected virtual void VisitValue( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ValueInfo info, string value, int offset) { var kind = MSBuildCompletionExtensions.InferValueKindIfUnknown(info); if (!kind.AllowExpressions()) { VisitValueExpression( element, attribute, resolvedElement, resolvedAttribute, info, kind, new ExpressionText(offset, value, true)); return; } var expression = ExpressionParser.Parse(value, kind.GetExpressionOptions(), offset); VisitValueExpression( element, attribute, resolvedElement, resolvedAttribute, info, kind, expression); }
protected override void VisitResolvedAttribute( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { switch (resolvedElement.SyntaxKind) { case MSBuildSyntaxKind.Import: if (attribute.NameEquals("Project", true)) { CaptureAnnotations(); } break; case MSBuildSyntaxKind.Project: if (attribute.NameEquals("Sdk", true)) { CaptureAnnotations(); } break; } base.VisitResolvedAttribute(element, attribute, resolvedElement, resolvedAttribute); void CaptureAnnotations() { var annotations = Document.Annotations.GetMany <NavigationAnnotation> (attribute); if (annotations != null) { foreach (var group in annotations.GroupBy(a => a.Span.Start)) { var first = group.First(); Navigations.Add(new MSBuildNavigationResult( group.Select(a => a.Path).ToArray(), first.Span.Start, first.Span.Length )); } } } }
protected virtual void VisitValue( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ITypedSymbol valueDescriptor, string value, int offset) { var kind = MSBuildCompletionExtensions.InferValueKindIfUnknown(valueDescriptor); if (!kind.AllowExpressions()) { VisitValueExpression( element, attribute, resolvedElement, resolvedAttribute, valueDescriptor, kind, new ExpressionText(offset, value, true)); return; } var expression = valueDescriptor?.ValueKind == MSBuildValueKind.Condition ? ExpressionParser.ParseCondition(value, offset) : ExpressionParser.Parse(value, kind.GetExpressionOptions(), offset); VisitValueExpression( element, attribute, resolvedElement, resolvedAttribute, valueDescriptor, kind, expression); }
protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ITypedSymbol valueDescriptor, MSBuildValueKind inferredKind, ExpressionNode node) { var nodeAtOffset = node.Find(offset); switch (nodeAtOffset) { case ExpressionItemName ei: rr.ReferenceKind = MSBuildReferenceKind.Item; rr.ReferenceOffset = ei.Offset; rr.ReferenceLength = ei.Name.Length; rr.Reference = ei.Name; break; case ExpressionPropertyName propName: rr.ReferenceKind = MSBuildReferenceKind.Property; rr.ReferenceOffset = propName.Offset; rr.Reference = propName.Name; rr.ReferenceLength = propName.Length; break; case ExpressionMetadata em: if (em.ItemName == null || offset >= em.MetadataNameOffset) { rr.ReferenceKind = MSBuildReferenceKind.Metadata; rr.ReferenceOffset = em.MetadataNameOffset; rr.Reference = (em.GetItemName(), em.MetadataName); rr.ReferenceLength = em.MetadataName.Length; } else { rr.ReferenceKind = MSBuildReferenceKind.Item; rr.ReferenceOffset = em.ItemNameOffset; rr.Reference = em.ItemName; rr.ReferenceLength = em.ItemName.Length; } break; case ExpressionFunctionName name: rr.ReferenceOffset = name.Offset; rr.ReferenceLength = name.Name.Length; switch (name.Parent) { case ExpressionItemNode _: rr.ReferenceKind = MSBuildReferenceKind.ItemFunction; rr.Reference = name.Name; break; case ExpressionPropertyFunctionInvocation prop: { if (prop.Target is ExpressionClassReference classRef) { rr.ReferenceKind = MSBuildReferenceKind.StaticPropertyFunction; rr.Reference = (classRef.Name, name.Name); } else if (prop.Target is ExpressionPropertyNode propNode) { var type = functionTypeProvider?.ResolveType(propNode) ?? MSBuildValueKind.Unknown; rr.ReferenceKind = MSBuildReferenceKind.PropertyFunction; rr.Reference = (type, name.Name); } break; } case ExpressionConditionFunction _: rr.ReferenceKind = MSBuildReferenceKind.ConditionFunction; rr.Reference = name.Name; break; } break; case ExpressionClassReference cr: if (!string.IsNullOrEmpty(cr.Name)) { if (cr.Parent is ExpressionArgumentList) { rr.ReferenceKind = MSBuildReferenceKind.Enum; } else if (cr.Parent is ExpressionPropertyFunctionInvocation) { rr.ReferenceKind = MSBuildReferenceKind.ClassName; } else { break; } rr.ReferenceOffset = cr.Offset; rr.Reference = cr.Name; rr.ReferenceLength = cr.Length; } break; case ExpressionText lit: inferredKind = inferredKind.GetScalarType(); if (lit.IsPure) { VisitPureLiteral(element, valueDescriptor, inferredKind, lit); if (inferredKind == MSBuildValueKind.TaskOutputParameterName) { rr.ReferenceKind = MSBuildReferenceKind.TaskParameter; rr.ReferenceOffset = lit.Offset; rr.ReferenceLength = lit.Value.Length; rr.Reference = (element.ParentElement.Name.Name, lit.Value); break; } } switch (inferredKind) { case MSBuildValueKind.File: case MSBuildValueKind.FileOrFolder: case MSBuildValueKind.ProjectFile: case MSBuildValueKind.TaskAssemblyFile: var pathNode = lit.Parent as ConcatExpression ?? (ExpressionNode)lit; var path = MSBuildNavigation.GetPathFromNode(pathNode, (MSBuildRootDocument)Document); if (path != null) { rr.ReferenceKind = MSBuildReferenceKind.FileOrFolder; rr.ReferenceOffset = path.Offset; rr.ReferenceLength = path.Length; rr.Reference = path.Paths; } break; } break; } }
public static MSBuildResolveResult Resolve( XmlSpineParser spineParser, ITextSource textSource, MSBuildDocument context, IFunctionTypeProvider functionTypeProvider, CancellationToken cancellationToken = default) { int offset = spineParser.Position; var nodePath = spineParser.AdvanceToNodeEndAndGetNodePath(textSource); nodePath.ConnectParents(); //need to look up element by walking how the path, since at each level, if the parent has special children, //then that gives us information to identify the type of its children MSBuildElementSyntax languageElement = null; MSBuildAttributeSyntax languageAttribute = null; XElement el = null; XAttribute att = null; foreach (var node in nodePath) { if (node is XAttribute xatt && xatt.Name.Prefix == null) { att = xatt; languageAttribute = languageElement?.GetAttribute(att.Name.Name); break; } //if children of parent is known to be arbitrary data, don't go into it if (languageElement != null && languageElement.ValueKind == MSBuildValueKind.Data) { break; } //code completion is forgiving, all we care about best guess resolve for deepest child if (node is XElement xel && xel.Name.Prefix == null) { el = xel; languageElement = MSBuildElementSyntax.Get(el.Name.Name, languageElement); if (languageElement != null) { continue; } } if (node is XText) { continue; } if (node is XClosingTag ct && ct == el.ClosingTag) { continue; } languageElement = null; } if (languageElement == null) { return(null); } var rr = new MSBuildResolveResult { ElementSyntax = languageElement, AttributeSyntax = languageAttribute, Element = el, Attribute = att }; var rv = new MSBuildResolveVisitor(offset, rr, functionTypeProvider); try { rv.Run(el, languageElement, textSource, context, token: cancellationToken); } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { // callers always have to handle the possibility this returns null // so this means callers don't need to handle cancellation exceptions explciitly return(null); } return(rr); }
protected virtual void VisitAttributeValue(XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, string value, int offset) { }
protected virtual void VisitValueExpression( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ITypedSymbol valueType, MSBuildValueKind inferredKind, ExpressionNode node) { }
protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { bool allowExpressions = kind.AllowExpressions(); bool allowLists = kind.AllowListsOrCommaLists(); if (node is ListExpression list) { if (!allowLists) { Document.Diagnostics.Add( CoreDiagnostics.UnexpectedList, new TextSpan(list.Nodes[0].End, list.End - list.Nodes[0].End), ImmutableDictionary <string, object> .Empty.Add("Name", info.Name), DescriptionFormatter.GetKindNoun(info), info.Name); } if (!allowExpressions) { var expr = list.Nodes.FirstOrDefault(n => !(n is ExpressionText)); if (expr != null) { AddExpressionWarning(expr); } } } else if (node is ExpressionText lit) { VisitPureLiteral(info, kind, lit.GetUnescapedValue(), lit.Offset); } else { if (!allowExpressions) { AddExpressionWarning(node); } } foreach (var n in node.WithAllDescendants()) { switch (n) { case ExpressionError err: var(desc, args) = CoreDiagnostics.GetExpressionError(err, info); Document.Diagnostics.Add(desc, new TextSpan(err.Offset, Math.Max(1, err.Length)), args); break; case ExpressionMetadata meta: case ExpressionProperty prop: case ExpressionItem item: //TODO: can we validate property/metadata/items refs? //maybe warn if they're not used anywhere outside of this expression? //TODO: deprecation squiggles in expressions break; } } void AddExpressionWarning(ExpressionNode n) => Document.Diagnostics.Add(CoreDiagnostics.UnexpectedExpression, new TextSpan(n.Offset, n.Length), DescriptionFormatter.GetKindNoun(info), info.Name); }
internal MSBuildAttribute(MSBuildElement parent, XAttribute xattribute, MSBuildAttributeSyntax syntax, ExpressionNode value) : base(parent, value) { XAttribute = xattribute; Syntax = syntax; }
void ValidateAttribute(XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { if (string.IsNullOrWhiteSpace(attribute.Value)) { if (resolvedAttribute.Required) { Document.Diagnostics.Add(CoreDiagnostics.RequiredAttributeEmpty, attribute.NameSpan, attribute.Name); } else { Document.Diagnostics.Add(CoreDiagnostics.AttributeEmpty, attribute.NameSpan, attribute.Name); } return; } }
protected override void VisitResolvedAttribute(XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { if (resolvedAttribute.IsAbstract) { switch (resolvedElement.SyntaxKind) { case MSBuildSyntaxKind.Item: CollectMetadata(element.Name.Name, attribute.Name.Name); break; case MSBuildSyntaxKind.Task: CollectTaskParameter(element.Name.Name, attribute.Name.Name, false); break; } } if (resolvedElement.SyntaxKind == MSBuildSyntaxKind.Output && resolvedAttribute.Name == "TaskParameter") { CollectTaskParameter(element.ParentElement.Name.Name, attribute.Value, true); } base.VisitResolvedAttribute(element, attribute, resolvedElement, resolvedAttribute); }
protected virtual void VisitValueExpression( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { }
protected override void VisitResolvedAttribute(XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { session.ExecuteAttributeActions(element, attribute, resolvedElement, resolvedAttribute); base.VisitResolvedAttribute(element, attribute, resolvedElement, resolvedAttribute); }
protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ITypedSymbol valueType, MSBuildValueKind kind, ExpressionNode node) { switch (kind.GetScalarType()) { case MSBuildValueKind.TargetName: foreach (var n in node.WithAllDescendants()) { if (n is ExpressionText lit && lit.IsPure) { Navigations.Add(new MSBuildNavigationResult( MSBuildReferenceKind.Target, lit.Value, lit.Offset, lit.Length )); } } break; case MSBuildValueKind.File: case MSBuildValueKind.FileOrFolder: case MSBuildValueKind.ProjectFile: case MSBuildValueKind.TaskAssemblyFile: case MSBuildValueKind.Unknown: if (node is ListExpression list) { foreach (var n in list.Nodes) { var p = GetPathFromNode(n, (MSBuildRootDocument)Document); if (p != null) { Navigations.Add(p); } } } var path = GetPathFromNode(node, (MSBuildRootDocument)Document); if (path != null) { Navigations.Add(path); } break; } }
public void ExecuteAttributeActions(XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { if (Context.GetAttributeActions(resolvedAttribute?.SyntaxKind ?? MSBuildSyntaxKind.Unknown, out var actions)) { var ctx = new AttributeDiagnosticContext(this, element, attribute, resolvedElement, resolvedAttribute); foreach (var(analyzer, action) in actions) { try { if (!analyzer.Disabled) { action(ctx); } } catch (Exception ex) { Context.ReportAnalyzerError(analyzer, ex); } } } }
protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { bool allowExpressions = kind.AllowExpressions(); bool allowLists = kind.AllowListsOrCommaLists(); if (node is ListExpression list) { if (!allowLists) { Document.Diagnostics.Add( CoreDiagnostics.UnexpectedList, new TextSpan(list.Nodes[0].End, list.End - list.Nodes[0].End), ImmutableDictionary <string, object> .Empty.Add("Name", info.Name), DescriptionFormatter.GetKindNoun(info), info.Name); } if (!allowExpressions) { var expr = list.Nodes.FirstOrDefault(n => !(n is ExpressionText)); if (expr != null) { AddExpressionWarning(expr); } } } else if (node is ExpressionText lit) { VisitPureLiteral(info, kind, lit.GetUnescapedValue(), lit.Offset); } else { if (!allowExpressions) { AddExpressionWarning(node); } } foreach (var n in node.WithAllDescendants()) { switch (n) { case ExpressionError err: var(desc, args) = CoreDiagnostics.GetExpressionError(err, info); Document.Diagnostics.Add(desc, new TextSpan(err.Offset, Math.Max(1, err.Length)), args); break; case ExpressionMetadata meta: var metaItem = meta.GetItemName(); if (!string.IsNullOrEmpty(metaItem) && !IsMetadataUsed(metaItem, meta.MetadataName, ReferenceUsage.Write)) { Document.Diagnostics.Add( CoreDiagnostics.UnwrittenMetadata, meta.Span, ImmutableDictionary <string, object> .Empty .Add("ItemName", metaItem) .Add("Name", meta.MetadataName) .Add("Spans", new [] { new TextSpan(meta.MetadataNameOffset, meta.MetadataName.Length) }), metaItem, meta.MetadataName ); } break; case ExpressionPropertyName prop: if (!IsPropertyUsed(prop.Name, ReferenceUsage.Write)) { Document.Diagnostics.Add( CoreDiagnostics.UnwrittenProperty, prop.Span, ImmutableDictionary <string, object> .Empty .Add("Name", prop.Name) .Add("Spans", new[] { new TextSpan(prop.Offset, prop.Length) }), prop.Name ); } break; case ExpressionItemName item: if (!IsItemUsed(item.Name, ReferenceUsage.Write)) { Document.Diagnostics.Add( CoreDiagnostics.UnwrittenItem, item.Span, ImmutableDictionary <string, object> .Empty .Add("Name", item.Name) .Add("Spans", new[] { new TextSpan(item.Offset, item.Length) }), item.Name ); } //TODO: deprecation squiggles in expressions break; } } void AddExpressionWarning(ExpressionNode n) => Document.Diagnostics.Add(CoreDiagnostics.UnexpectedExpression, new TextSpan(n.Offset, n.Length), DescriptionFormatter.GetKindNoun(info), info.Name); }
protected override void VisitResolvedAttribute(XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { if (resolvedAttribute.SyntaxKind == MSBuildSyntaxKind.Item_Metadata) { if (!IsMetadataUsed(element.Name.Name, attribute.Name.Name, ReferenceUsage.Read)) { Document.Diagnostics.Add( CoreDiagnostics.UnreadMetadata, attribute.NameSpan, ImmutableDictionary <string, object> .Empty .Add("ItemName", element.Name.Name) .Add("Name", attribute.Name.Name) .Add("Spans", new[] { attribute.NameSpan }), element.Name.Name, attribute.Name.Name ); } } ValidateAttribute(element, attribute, resolvedElement, resolvedAttribute); base.VisitResolvedAttribute(element, attribute, resolvedElement, resolvedAttribute); }
protected override void VisitValueExpression(XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { if (resolvedElement.SyntaxKind == MSBuildSyntaxKind.Property && resolvedAttribute == null) { session.ExecutePropertyWriteActions(element, info, kind, node); } base.VisitValueExpression(element, attribute, resolvedElement, resolvedAttribute, info, kind, node); }
protected override void VisitAttributeValue(XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute, string value, int offset) { var kind = resolvedAttribute.ValueKind; //FIXME ExtractConfigurations should directly handle extracting references if (kind == MSBuildValueKind.Condition) { ExtractConfigurations(value, offset); } if (resolvedElement.SyntaxKind == MSBuildSyntaxKind.Item && element.NameEquals("ProjectConfiguration", true)) { if (attribute.NameEquals("Configuration", true)) { kind = MSBuildValueKind.Configuration; } else if (attribute.NameEquals("Platform", true)) { kind = MSBuildValueKind.Platform; } } ExtractReferences(kind, value, offset); }