protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildLanguageElement resolvedElement, MSBuildLanguageAttribute resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { if (kind.GetScalarType() != MSBuildValueKind.TargetName) { return; } bool isDeclaration = !kind.AllowExpressions(); switch (node) { case ListExpression list: foreach (var c in list.Nodes) { if (c is ExpressionText l) { CheckMatch(l, isDeclaration); } } break; case ExpressionText lit: CheckMatch(lit, isDeclaration); break; } }
protected override void VisitResolvedAttribute( XElement element, XAttribute attribute, MSBuildLanguageElement resolvedElement, MSBuildLanguageAttribute resolvedAttribute) { VisitAttributeValue(element, attribute, resolvedElement, resolvedAttribute); base.VisitResolvedAttribute(element, attribute, resolvedElement, resolvedAttribute); }
protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildLanguageElement resolvedElement, MSBuildLanguageAttribute resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { //these are things like <Foo Include="@(Bar)" RemoveMetadata="SomeBarMetadata" /> if (kind.GetScalarType() == MSBuildValueKind.MetadataName) { var expr = GetIncludeExpression(element); if (expr != null && expr .WithAllDescendants() .OfType <ExpressionItemName> () .Any(n => IsItemNameMatch(n.ItemName)) ) { switch (node) { case ListExpression list: foreach (var c in list.Nodes) { if (c is ExpressionText l) { CheckMatch(l); break; } } break; case ExpressionText lit: CheckMatch(lit); break; } } void CheckMatch(ExpressionText t) { //FIXME: get rid of this trim if (t.IsPure && IsMatch(t.Value.Trim())) { Results.Add((t.Offset, t.Length, ReferenceUsage.Read)); } } return; } foreach (var n in node.WithAllDescendants()) { switch (n) { case ExpressionMetadata em: var iname = em.GetItemName(); if (iname != null && IsItemNameMatch(iname) && IsMatch(em.MetadataName)) { Results.Add((em.MetadataNameOffset, em.MetadataName.Length, ReferenceUsage.Read)); } break; } } }
public void Run(XElement element, MSBuildLanguageElement resolvedElement, string filename, ITextSource textDocument, MSBuildDocument document, int offset = 0, int length = 0) { Filename = filename; Document = document; Extension = System.IO.Path.GetExtension(filename); //HACK: we should really use the ITextSource directly, but since the XML parser positions are //currently line/col, we need a TextDocument to convert to offsets TextDocument = textDocument as IReadonlyTextDocument ?? TextEditorFactory.CreateNewReadonlyDocument( textDocument, filename, MSBuildTextEditorExtension.MSBuildMimeType ); range = new DocumentRegion( TextDocument.OffsetToLocation(offset), length > 0 ? TextDocument.OffsetToLocation(length + offset) : new DocumentLocation(int.MaxValue, int.MaxValue)); if (resolvedElement != null) { VisitResolvedElement(element, resolvedElement); } else if (element != null) { ResolveAndVisit(element, null); } }
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 = MSBuildLanguageElement.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); }
public ElementDiagnosticContext(MSBuildDocument document, MSBuildLanguageElement element, Action <MSBuildDiagnostic> reportDiagnostic, CancellationToken cancellationToken) { Document = document; Element = element; this.reportDiagnostic = reportDiagnostic; CancellationToken = cancellationToken; }
void ResolveAttributesAndValue(XElement element, MSBuildLanguageElement resolved) { foreach (var att in element.Attributes) { if (att.Region.End < range.Begin) { continue; } if (att.Region.Begin > 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; } }
public void Build( XDocument doc, ITextDocument textDocument, IRuntimeInformation runtime, PropertyValueCollector propVals, TaskMetadataBuilder taskBuilder, ImportResolver resolveImport) { var project = doc.Nodes.OfType <XElement> ().FirstOrDefault(x => x.Name == xnProject); if (project == null) { //TODO: error return; } var sdks = ResolveSdks(runtime, project, textDocument).ToList(); var pel = MSBuildLanguageElement.Get("Project"); GetPropertiesToTrack(propVals, project); AddSdkProps(sdks, propVals, resolveImport); var resolver = new MSBuildSchemaBuilder(IsToplevel, runtime, propVals, taskBuilder, resolveImport); resolver.Run(doc, Filename, textDocument, this); AddSdkTargets(sdks, propVals, resolveImport); }
void VisitElementValue(XElement element, MSBuildLanguageElement resolved) { if (element.IsSelfClosing || !element.IsEnded) { return; } var begin = element.Span.End; int end = begin; if (element.IsClosed && element.FirstChild == null) { end = element.ClosingTag.Span.Start; } else { //HACK: in some cases GetCharAt can throw at the end of the document even with TextDocument.Length check try { for (; end < (TextSource.Length + 1) && TextSource.GetCharAt(end) != '<'; end++) { } } catch { end--; } } var text = TextSource.GetTextBetween(begin, end); VisitElementValue(element, resolved, text, begin); }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { switch (resolved.SyntaxKind) { case MSBuildSyntaxKind.Task: if (IsMatch(element.Name.Name)) { Results.Add((element.NameOffset, element.Name.Name.Length, ReferenceUsage.Read)); } break; case MSBuildSyntaxKind.UsingTask: var nameAtt = element.Attributes.Get(new XName("TaskName"), true); if (nameAtt != null && !string.IsNullOrEmpty(nameAtt.Value)) { var nameIdx = nameAtt.Value.LastIndexOf('.') + 1; string name = nameIdx > 0 ? nameAtt.Value.Substring(nameIdx) : nameAtt.Value; if (IsMatch(name)) { Results.Add((nameAtt.ValueOffset + nameIdx, name.Length, ReferenceUsage.Declaration)); } } break; } base.VisitResolvedElement(element, resolved); }
protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildLanguageElement resolvedElement, MSBuildLanguageAttribute resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { foreach (var n in node.WithAllDescendants()) { switch (n) { case ExpressionFunctionName func: if (func.Parent is ExpressionPropertyFunctionInvocation inv) { string baseName = StripGetPrefix(func.Name); if (IsMatch(baseName)) { //TODO: should we be fuzzy here and accept "unknown"? var resolvedKind = functionTypeProvider.ResolveType(inv); if (resolvedKind == MSBuildValueKind.Unknown) { resolvedKind = MSBuildValueKind.String; } if (resolvedKind == valueKind) { Results.Add((func.Offset, func.Length, ReferenceUsage.Read)); } } } break; } } }
protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildLanguageElement resolvedElement, MSBuildLanguageAttribute resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { foreach (var n in node.WithAllDescendants()) { switch (n) { case ExpressionItemName ei: if (IsMatch(ei.Name)) { Results.Add((ei.Offset, ei.Name.Length, ReferenceUsage.Read)); } break; case ExpressionMetadata em: if (em.IsQualified && IsMatch(em.ItemName)) { Results.Add((em.ItemNameOffset, em.ItemName.Length, ReferenceUsage.Read)); } break; } } }
void VisitElementValue(XElement element, MSBuildLanguageElement resolved) { if (element.FirstChild != null || resolved.ValueKind == MSBuildValueKind.Data || resolved.ValueKind == MSBuildValueKind.Nothing) { return; } if (element.IsSelfClosing || !(element.ClosingTag is XClosingTag closing)) { return; } var begin = element.Span.End; int end = element.ClosingTag.Span.Start; var value = TextSource.GetTextBetween(begin, end); 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, begin); }
protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildLanguageElement resolvedElement, MSBuildLanguageAttribute resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { if (kind.GetScalarType() != MSBuildValueKind.TargetName) { return; } switch (node) { case ExpressionList list: foreach (var c in list.Nodes) { if (c is ExpressionLiteral l) { CheckMatch(l); break; } } break; case ExpressionLiteral lit: CheckMatch(lit); break; } }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { if ((resolved.Kind == MSBuildKind.Property) && IsMatch(element.Name.Name)) { Results.Add((element.GetNameStartOffset(TextDocument), element.Name.Name.Length, ReferenceUsage.Write)); } base.VisitResolvedElement(element, resolved); }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { if (resolved.SyntaxKind == MSBuildSyntaxKind.Metadata && IsMatch(element.Name.Name) && IsItemNameMatch(element.ParentElement.Name.Name)) { Results.Add((element.NameOffset, element.Name.Name.Length, ReferenceUsage.Write)); } base.VisitResolvedElement(element, resolved); }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { if (resolved.Kind == MSBuildKind.Metadata && IsMatch(element.Name.Name) && IsItemNameMatch(element.ParentElement().Name.Name)) { AddNameResult(element, ReferenceUsage.Write); } base.VisitResolvedElement(element, resolved); }
protected virtual void VisitResolvedAttribute( XElement element, XAttribute attribute, MSBuildLanguageElement resolvedElement, MSBuildLanguageAttribute resolvedAttribute) { if (attribute.Value != null) { VisitAttributeValue(element, attribute, resolvedElement, resolvedAttribute, attribute.Value, attribute.GetValueStartOffset(TextDocument)); } }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { try { ValidateResolvedElement(element, resolved); base.VisitResolvedElement(element, resolved); } catch (Exception ex) { AddError($"Internal error: {ex.Message}", element.GetNameRegion()); LoggingService.LogError("Internal error in MSBuildDocumentValidator", ex); } }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { foreach (var rat in resolved.Attributes) { if (rat.Required && !rat.IsAbstract) { var xat = element.Attributes.Get(new XName(rat.Name), true); if (xat == null) { AddError($"{element.Name.Name} must have attribute {rat.Name}", element.GetNameRegion()); } } } switch (resolved.Kind) { case MSBuildKind.Project: if (!Filename.EndsWith(".props", StringComparison.OrdinalIgnoreCase)) { ValidateProjectHasTarget(element); } break; case MSBuildKind.OnError: ValidateOnErrorOnlyFollowedByOnError(element); break; case MSBuildKind.Otherwise: ValidateOtherwiseIsLastElement(element); break; case MSBuildKind.Output: ValidateOutputHasPropertyOrItemName(element); break; case MSBuildKind.UsingTask: ValidateUsingTaskHasAssembly(element); break; case MSBuildKind.Import: ValidateImportOnlyHasVersionIfHasSdk(element); break; case MSBuildKind.Item: ValidateItemAttributes(resolved, element); break; case MSBuildKind.Task: ValidateTaskParameters(resolved, element); break; } base.VisitResolvedElement(element, resolved); }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { try { CollectResolvedElement(element, resolved); base.VisitResolvedElement(element, resolved); } catch (Exception ex) when(isToplevel) { Document.Errors.Add(new XmlDiagnosticInfo(DiagnosticSeverity.Error, $"Internal error: {ex.Message}", element.NameSpan)); LoggingService.LogError("Internal error in MSBuildDocumentValidator", ex); } }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { if (resolved.Kind == MSBuildKind.Target) { var nameAtt = element.Attributes.Get(new XName("Name"), true); if (nameAtt != null && IsMatch(nameAtt.Value)) { AddValueResult(nameAtt, ReferenceUsage.Declaration); } } base.VisitResolvedElement(element, resolved); }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { if (resolved.SyntaxKind == MSBuildSyntaxKind.Target) { var nameAtt = element.Attributes.Get(new XName("Name"), true); if (nameAtt != null && IsMatch(nameAtt.Value)) { Results.Add((nameAtt.Span.Start, nameAtt.Span.Length, ReferenceUsage.Declaration)); } } base.VisitResolvedElement(element, resolved); }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { if (resolved.Kind == MSBuildKind.Target) { var nameAtt = element.Attributes.Get(new XName(Name), true); if (nameAtt != null && IsMatch(nameAtt.Value)) { Results.Add((nameAtt.GetValueStartOffset(TextDocument), Name.Length, ReferenceUsage.Declaration)); } } base.VisitResolvedElement(element, resolved); }
protected virtual void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { ResolveAttributesAndValue(element, resolved); if (resolved.ValueKind == MSBuildValueKind.Nothing) { foreach (var child in element.Elements) { ResolveAndVisit(child, resolved); } } }
void ValidateResolvedElement(XElement element, MSBuildLanguageElement resolved) { foreach (var rat in resolved.Attributes) { if (rat.Required && !rat.IsAbstract) { var xat = element.Attributes.Get(new XName(rat.Name), true); if (xat == null) { AddError($"{element.Name.Name} must have attribute {rat.Name}", element.NameSpan); } } } 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; } }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement 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(new XName("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); }
void ResolveAndVisit(XElement element, MSBuildLanguageElement parent) { var resolved = MSBuildLanguageElement.Get(element.Name.Name, parent); if (resolved != null) { VisitResolvedElement(element, resolved); } else { VisitUnknownElement(element); } }
protected override void VisitResolvedElement(XElement element, MSBuildLanguageElement resolved) { try { ValidateResolvedElement(element, resolved); //don't validate children of incomplete elements if (element.IsComplete) { base.VisitResolvedElement(element, resolved); } } catch (Exception ex) { AddError($"Internal error: {ex.Message}", element.NameSpan); LoggingService.LogError("Internal error in MSBuildDocumentValidator", ex); } }
protected override void VisitValueExpression( XElement element, XAttribute attribute, MSBuildLanguageElement resolvedElement, MSBuildLanguageAttribute resolvedAttribute, ValueInfo info, MSBuildValueKind kind, ExpressionNode node) { bool allowExpressions = kind.AllowExpressions(); bool allowLists = kind.AllowListsOrCommaLists(); foreach (var n in node.WithAllDescendants()) { switch (n) { case ExpressionList list: if (!allowLists) { AddListWarning(list.Nodes [0].End, 1); } break; case ExpressionError err: var msg = err.Kind.GetMessage(info, out bool isWarning); AddError( isWarning? ErrorType.Warning : ErrorType.Error, msg, err.Offset, Math.Max(1, err.Length) ); break; case ExpressionMetadata meta: case ExpressionProperty prop: case ExpressionItem item: if (!allowExpressions) { AddExpressionWarning(node); } //TODO: can we validate property/metadata/items refs? //maybe warn if they're not used anywhere outside of this expression? break; case ExpressionText lit: VisitPureLiteral(info, kind, lit.GetUnescapedValue(), lit.Offset); break; } } string Name() => info.GetTitleCaseKindName(); void AddExpressionWarning(ExpressionNode n) => AddWarning($"{Name ()} does not expect expressions", n.Offset, n.Length); void AddListWarning(int start, int length) => AddWarning($"{Name ()} does not expect lists", start, length); }