protected override void VisitResolvedAttribute( XElement element, XAttribute attribute, MSBuildElementSyntax resolvedElement, MSBuildAttributeSyntax resolvedAttribute) { VisitAttributeValue(element, attribute, resolvedElement, resolvedAttribute); base.VisitResolvedAttribute(element, attribute, resolvedElement, resolvedAttribute); }
public void Build( XDocument doc, ITextSource textSource, MSBuildParserContext context) { var project = doc.Nodes.OfType <XElement> ().FirstOrDefault(x => x.Name == xnProject); if (project == null) { //TODO: error return; } var sdks = ResolveSdks(context, project).ToList(); var pel = MSBuildElementSyntax.Get("Project"); GetPropertiesToTrack(context.PropertyCollector, project); var importResolver = context.CreateImportResolver(Filename); AddSdkProps(sdks, context.PropertyCollector, importResolver); var resolver = new MSBuildSchemaBuilder(IsToplevel, context, importResolver); resolver.Run(doc, textSource, this); AddSdkTargets(sdks, context.PropertyCollector, importResolver); }
void ValidateItemAttributes(MSBuildElementSyntax resolved, XElement element) { bool isInTarget = resolved.IsInTarget(element); bool hasInclude = false, hasUpdate = false, hasRemove = false; foreach (var att in element.Attributes) { hasInclude |= att.NameEquals("Include", true); hasRemove |= att.NameEquals("Remove", true); if (att.NameEquals("Update", true)) { hasUpdate = true; if (isInTarget) { Document.Diagnostics.Add(CoreDiagnostics.ItemAttributeNotValidInTarget, att.NameSpan, att.Name.Name); } } if (att.NameEquals("KeepMetadata", true) || att.NameEquals("RemoveMetadata", true) || att.NameEquals("KeepDuplicates", true)) { if (!isInTarget) { Document.Diagnostics.Add(CoreDiagnostics.ItemAttributeOnlyValidInTarget, att.NameSpan, att.Name.Name); } } } if (!hasInclude && !hasRemove && !hasUpdate && !isInTarget) { Document.Diagnostics.Add(CoreDiagnostics.ItemMustHaveInclude, element.NameSpan); } }
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); }
void ValidateResolvedElement(XElement element, MSBuildElementSyntax resolved) { foreach (var rat in resolved.Attributes) { if (rat.Required && !rat.IsAbstract) { var xat = element.Attributes.Get(rat.Name, true); if (xat == null) { Document.Diagnostics.Add(CoreDiagnostics.MissingRequiredAttribute, element.NameSpan, element.NameSpan, rat.Name); } } } switch (resolved.SyntaxKind) { case MSBuildSyntaxKind.Project: if (!IsPropsFile) { ValidateProjectHasTarget(element); } break; case MSBuildSyntaxKind.OnError: ValidateOnErrorOnlyFollowedByOnError(element); break; case MSBuildSyntaxKind.Otherwise: ValidateOtherwiseIsLastElement(element); break; case MSBuildSyntaxKind.Output: ValidateOutputHasPropertyOrItemName(element); break; case MSBuildSyntaxKind.UsingTask: ValidateUsingTaskHasAssembly(element); break; case MSBuildSyntaxKind.Import: ValidateImportOnlyHasVersionIfHasSdk(element); break; case MSBuildSyntaxKind.Item: ValidateItemAttributes(resolved, element); break; case MSBuildSyntaxKind.Task: ValidateTaskParameters(resolved, element); break; } if (resolved.ValueKind == MSBuildValueKind.Nothing) { foreach (var txt in element.Nodes.OfType <XText> ()) { Document.Diagnostics.Add(CoreDiagnostics.UnexpectedText, txt.Span, element.Name.Name); } } }
void ResolveAttributesAndValue(XElement element, MSBuildElementSyntax resolved) { foreach (var att in element.Attributes) { if (att.Span.End < range.Start) { continue; } if (att.Span.Start > range.End) { return; } var resolvedAtt = resolved.GetAttribute(att.Name.FullName); if (resolvedAtt != null) { VisitResolvedAttribute(element, att, resolved, resolvedAtt); continue; } VisitUnknownAttribute(element, att); } if (resolved.ValueKind != MSBuildValueKind.Nothing && resolved.ValueKind != MSBuildValueKind.Data) { VisitElementValue(element, resolved); return; } }
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); } }
protected override async Task <CompletionContext> GetElementCompletionsAsync( IAsyncCompletionSession session, SnapshotPoint triggerLocation, List <XObject> nodePath, bool includeBracket, CancellationToken token) { var context = await GetSessionContext(session, triggerLocation, token); var doc = context.doc; // we can't use the LanguageElement from the resolveresult here. // if completion is triggered in an existing element's name, the resolveresult // will be for that element, so completion will be for the element's children // rather than for the element itself. MSBuildElementSyntax languageElement = null; string elName = null; for (int i = 1; i < nodePath.Count; i++) { if (nodePath[i] is XElement el) { elName = el.Name.Name; languageElement = MSBuildElementSyntax.Get(elName, languageElement); continue; } return(CompletionContext.Empty); } if (languageElement == null && nodePath.Count > 0) { return(CompletionContext.Empty); } var items = new List <CompletionItem> (); foreach (var el in doc.GetElementCompletions(languageElement, elName)) { if (el is ItemInfo) { items.Add(CreateCompletionItem(el, XmlCompletionItemKind.SelfClosingElement, includeBracket ? "<" : null)); } else { items.Add(CreateCompletionItem(el, XmlCompletionItemKind.Element, includeBracket ? "<" : null)); } } bool allowcData = languageElement != null && languageElement.ValueKind != MSBuildValueKind.Nothing; foreach (var c in GetMiscellaneousTags(triggerLocation, nodePath, includeBracket, allowcData)) { items.Add(c); } return(CreateCompletionContext(items)); }
internal ElementDiagnosticContext( MSBuildAnalysisSession session, XElement element, MSBuildElementSyntax elementSyntax) { this.session = session; Element = element; ElementSyntax = elementSyntax; }
protected override void VisitResolvedElement(XElement element, MSBuildElementSyntax resolved) { try { CollectResolvedElement(element, resolved); base.VisitResolvedElement(element, resolved); } catch (Exception ex) when(isToplevel && IsNotCancellation(ex)) { Document.Diagnostics?.Add(CoreDiagnostics.InternalError, element.NameSpan, ex.Message); LoggingService.LogError("Internal error in MSBuildDocumentValidator", ex); } }
protected override void VisitResolvedElement(XElement element, MSBuildElementSyntax resolved) { var start = element.NameOffset; bool inName = element.IsNamed && IsIn(start, element.Name.Name.Length); if (inName) { rr.ReferenceOffset = start; rr.Reference = element.Name.Name; rr.ReferenceLength = element.Name.Name.Length; switch (resolved.SyntaxKind) { case MSBuildSyntaxKind.Item: case MSBuildSyntaxKind.ItemDefinition: rr.ReferenceKind = MSBuildReferenceKind.Item; return; case MSBuildSyntaxKind.Metadata: rr.ReferenceKind = MSBuildReferenceKind.Metadata; rr.Reference = (element.ParentElement.Name.Name, element.Name.Name); return; case MSBuildSyntaxKind.Task: rr.ReferenceKind = MSBuildReferenceKind.Task; return; case MSBuildSyntaxKind.Parameter: var taskName = element.ParentElement.ParentElement.Attributes.Get("TaskName", true)?.Value; if (!string.IsNullOrEmpty(taskName)) { taskName = taskName.Substring(taskName.LastIndexOf('.') + 1); rr.ReferenceKind = MSBuildReferenceKind.TaskParameter; rr.Reference = (taskName, element.Name.Name); } return; case MSBuildSyntaxKind.Property: rr.ReferenceKind = MSBuildReferenceKind.Property; return; default: if (!resolved.IsAbstract) { rr.ReferenceKind = MSBuildReferenceKind.Keyword; rr.Reference = resolved; } return; } } base.VisitResolvedElement(element, resolved); }
protected override void VisitElementValue(XElement element, MSBuildElementSyntax resolved, string value, int offset) { base.VisitElementValue(element, resolved, value, offset); var info = Document.GetSchemas().GetElementInfo(resolved, (element.Parent as XElement)?.Name.Name, element.Name.Name, true); if (info == null) { return; } VisitValue(element, null, resolved, null, info, value, offset); }
internal AttributeDiagnosticContext( MSBuildAnalysisSession session, XElement element, XAttribute attribute, MSBuildElementSyntax elementSyntax, MSBuildAttributeSyntax attributeSyntax) { this.session = session; Element = element; Attribute = attribute; ElementSyntax = elementSyntax; AttributeSyntax = attributeSyntax; }
public override Task RegisterRefactoringsAsync(MSBuildRefactoringContext context) { if (context.SelectedSpan.Length > 0) { return(Task.CompletedTask); } XElement element; switch (context.ElementSyntax?.SyntaxKind) { case MSBuildSyntaxKind.Item: element = context.XObject.SelfAndParentsOfType <XElement> ().First(); break; case MSBuildSyntaxKind.Metadata: element = context.XObject.SelfAndParentsOfType <XElement> ().Skip(1).First(); break; case MSBuildSyntaxKind.Property: element = context.XObject.SelfAndParentsOfType <XElement> ().First(); break; default: return(Task.CompletedTask); } var group = (XElement)element.Parent; XElement previousElement = null; foreach (var c in group.Elements) { if (c == element) { break; } previousElement = c; } if (previousElement == null) { return(Task.CompletedTask); } //check name is cased correctly var groupName = MSBuildElementSyntax.Get(group.Name.FullName)?.Name; context.RegisterRefactoring(new SplitGroupAction(previousElement, groupName)); return(Task.CompletedTask); }
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 VisitResolvedElement(XElement element, MSBuildElementSyntax resolved) { try { ValidateResolvedElement(element, resolved); //don't validate children of incomplete elements if (element.IsComplete) { base.VisitResolvedElement(element, resolved); } } catch (Exception ex) when(!(ex is OperationCanceledException && CancellationToken.IsCancellationRequested)) { Document.Diagnostics.Add(CoreDiagnostics.InternalError, element.NameSpan, ex.Message); LoggingService.LogError("Internal error in MSBuildDocumentValidator", ex); } }
void CollectResolvedElement(XElement element, MSBuildElementSyntax resolved) { switch (resolved.SyntaxKind) { case MSBuildSyntaxKind.Import: ResolveImport(element); break; case MSBuildSyntaxKind.Item: CollectItem(element.Name.Name); break; case MSBuildSyntaxKind.Property: CollectProperty(element.Name.Name); break; case MSBuildSyntaxKind.UsingTask: CollectTaskDefinition(element); break; case MSBuildSyntaxKind.Task: CollectTask(element.Name.Name); break; case MSBuildSyntaxKind.Target: var targetName = element.Attributes.Get("name", true)?.Value; if (!string.IsNullOrEmpty(targetName)) { CollectTarget(targetName); } break; case MSBuildSyntaxKind.Parameter: var taskName = element.ParentElement.ParentElement.Attributes.Get("TaskName", true)?.Value; if (!string.IsNullOrEmpty(taskName)) { CollectTaskParameterDefinition(taskName, element); } break; case MSBuildSyntaxKind.Metadata: CollectMetadata(element.ParentElement.Name.Name, element.Name.Name); 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 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 ResolveAndVisit(XElement element, MSBuildElementSyntax parent) { CheckCancellation(); if (!element.Name.IsValid) { return; } var resolved = MSBuildElementSyntax.Get(element.Name.Name, parent); if (resolved != null) { VisitResolvedElement(element, resolved); } else { VisitUnknownElement(element); } }
protected override void VisitElementValue(XElement element, MSBuildElementSyntax resolved, string value, int offset) { var kind = resolved.ValueKind; if (resolved.SyntaxKind == MSBuildSyntaxKind.Property) { var name = element.Name.Name; parseContext.PropertyCollector.Collect(name, value); switch (element.Name.Name.ToLowerInvariant()) { case "configuration": kind = MSBuildValueKind.Configuration; break; case "configurations": kind = MSBuildValueKind.Configuration.List(); break; case "platform": kind = MSBuildValueKind.Platform; break; case "platforms": kind = MSBuildValueKind.Platform.List(); break; } } else if (resolved.SyntaxKind == MSBuildSyntaxKind.Metadata && element.ParentElement.NameEquals("ProjectConfiguration", true)) { if (element.NameEquals("Configuration", true)) { kind = MSBuildValueKind.Configuration; } else if (element.NameEquals("Platform", true)) { kind = MSBuildValueKind.Platform; } } ExtractReferences(kind, value, offset); }
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; } }
void VisitElementValue(XElement element, MSBuildElementSyntax resolved) { if (element.IsSelfClosing || !element.IsEnded) { return; } //FIXME: handle case with multiple text nodes with comments between them string value = string.Empty; var begin = element.Span.End; var textNode = element.Nodes.OfType <XText> ().FirstOrDefault(); if (textNode != null) { begin = textNode.Span.Start; value = TextSource.GetTextBetween(begin, textNode.Span.End); } VisitElementValue(element, resolved, value, begin); }
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 VisitResolvedElement(XElement element, MSBuildElementSyntax resolved) { ResolveAttributesAndValue(element, resolved); if (resolved.ValueKind == MSBuildValueKind.Nothing) { foreach (var child in element.Elements) { if ((child.ClosingTag ?? child).Span.End < range.Start) { continue; } if (child.Span.Start > range.End) { return; } ResolveAndVisit(child, resolved); } } }
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); }
public void Run( XElement element, MSBuildElementSyntax resolvedElement, ITextSource textSource, MSBuildDocument document, int offset = 0, int length = 0, CancellationToken token = default) { Document = document; Extension = Filename == null ? ".props" : System.IO.Path.GetExtension(Filename); TextSource = textSource; CancellationToken = token; range = new TextSpan(offset, length > 0 ? length + offset : int.MaxValue); if (resolvedElement != null) { VisitResolvedElement(element, resolvedElement); } else if (element != null) { ResolveAndVisit(element, null); } }
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); }