public static (MSBuildDiagnosticDescriptor, object[]) GetExpressionError(ExpressionError error, ValueInfo info)
        {
            (MSBuildDiagnosticDescriptor, object[]) Return(MSBuildDiagnosticDescriptor desc, params object[] args) => (desc, args);

            return(error.Kind switch
            {
                ExpressionErrorKind.MetadataDisallowed => Return(MetadataDisallowed, DescriptionFormatter.GetKindNoun(info), info.Name),
                ExpressionErrorKind.EmptyListEntry => Return(EmptyListValue),
                ExpressionErrorKind.ExpectingItemName => Return(ExpectingItemName),
                ExpressionErrorKind.ExpectingRightParen => Return(ExpectingChars1, ')'),
                ExpressionErrorKind.ExpectingRightParenOrPeriod => Return(ExpectingChars2, ')', '.'),
                ExpressionErrorKind.ExpectingPropertyName => Return(ExpectingPropertyName),
                ExpressionErrorKind.ExpectingMetadataName => Return(ExpectingMetadataName),
                ExpressionErrorKind.ExpectingMetadataOrItemName => Return(ExpectingMetadataOrItemName),
                ExpressionErrorKind.ExpectingRightAngleBracket => Return(ExpectingChars1, '>'),
                ExpressionErrorKind.ExpectingRightParenOrDash => Return(ExpectingChars2, ')', '-'),
                ExpressionErrorKind.ItemsDisallowed => Return(ItemsDisallowed, DescriptionFormatter.GetKindNoun(info), info.Name),
                ExpressionErrorKind.ExpectingMethodOrTransform => Return(ExpectingFunctionOrTransform),
                ExpressionErrorKind.ExpectingMethodName => Return(ExpectingFunctionName),
                ExpressionErrorKind.ExpectingLeftParen => Return(ExpectingChars1, '('),
                ExpressionErrorKind.ExpectingRightParenOrComma => Return(ExpectingChars2, ')', ','),
                ExpressionErrorKind.ExpectingRightParenOrValue => Return(ExpectingRightParenOrValue),
                ExpressionErrorKind.ExpectingValue => Return(ExpectingValue),
                ExpressionErrorKind.CouldNotParseNumber => Return(CouldNotParseNumber),
                ExpressionErrorKind.IncompleteValue => Return(IncompleteValue),
                ExpressionErrorKind.ExpectingBracketColonColon => Return(ExpectingChars1, "]::"),
                ExpressionErrorKind.ExpectingClassName => Return(ExpectingClassName),
                ExpressionErrorKind.ExpectingClassNameComponent => Return(IncompleteClassName),
                ExpressionErrorKind.IncompleteString => Return(IncompleteString),
                ExpressionErrorKind.IncompleteProperty => Return(IncompleteProperty),
                _ => throw new System.Exception($"Unhandled ExpressionErrorKind '{error.Kind}'")
            });
        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, StringComparison.OrdinalIgnoreCase))
                {
                    Document.Diagnostics.Add(
                        CoreDiagnostics.HasDefaultValue, attribute?.Span ?? element.OuterSpan,
                        ImmutableDictionary <string, object> .Empty.Add("Info", info),
                        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);
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        public override Task RegisterCodeFixesAsync(MSBuildFixContext context)
        {
            foreach (var diag in context.Diagnostics)
            {
                if (!(diag.Properties.TryGetValue("Info", out var val) && val is VariableInfo info))
                {
                    continue;
                }

                switch (context.XDocument.FindAtOffset(diag.Span.Start))
                {
                case XElement el:
                    context.RegisterCodeFix(new RemoveRedundantElementAction(el, DescriptionFormatter.GetKindNoun(info)), diag);
                    break;

                case XAttribute att:
                    context.RegisterCodeFix(new RemoveRedundantAttributeAction(att, DescriptionFormatter.GetKindNoun(info)), diag);
                    break;
                }
            }
            return(Task.CompletedTask);
        }
Ejemplo n.º 5
0
 void CheckDeprecated(ValueInfo info, INamedXObject namedObj)
 {
     if (info.IsDeprecated)
     {
         if (string.IsNullOrEmpty(info.DeprecationMessage))
         {
             Document.Diagnostics.Add(
                 CoreDiagnostics.Deprecated,
                 namedObj.NameSpan,
                 DescriptionFormatter.GetKindNoun(info),
                 info.Name);
         }
         else
         {
             Document.Diagnostics.Add(
                 CoreDiagnostics.DeprecatedWithMessage,
                 namedObj.NameSpan,
                 DescriptionFormatter.GetKindNoun(info),
                 info.Name,
                 info.DeprecationMessage
                 );
         }
     }
 }
Ejemplo n.º 6
0
        //note: the value is unescaped, so offsets within it are not valid
        void VisitPureLiteral(ValueInfo info, MSBuildValueKind kind, string value, int offset)
        {
            IReadOnlyList <ConstantInfo> knownVals = info.Values ?? kind.GetSimpleValues(false);

            if (knownVals != null && knownVals.Count != 0)
            {
                foreach (var kv in knownVals)
                {
                    if (string.Equals(kv.Name, value, StringComparison.OrdinalIgnoreCase))
                    {
                        return;
                    }
                }
                AddErrorWithArgs(CoreDiagnostics.UnknownValue, DescriptionFormatter.GetKindNoun(info), info.Name, value);
                return;
            }
            switch (kind)
            {
            case MSBuildValueKind.Guid:
            case MSBuildValueKind.ProjectKindGuid:
                if (!Guid.TryParseExact(value, "B", out _))
                {
                    AddErrorWithArgs(CoreDiagnostics.InvalidGuid, value);
                }
                break;

            case MSBuildValueKind.Int:
                if (!long.TryParse(value, out _))
                {
                    AddErrorWithArgs(CoreDiagnostics.InvalidInteger, value);
                }
                break;

            case MSBuildValueKind.Bool:
                if (!bool.TryParse(value, out _))
                {
                    AddErrorWithArgs(CoreDiagnostics.InvalidBool, value);
                }
                break;

            case MSBuildValueKind.Url:
                if (!Uri.TryCreate(value, UriKind.Absolute, out _))
                {
                    AddErrorWithArgs(CoreDiagnostics.InvalidUrl, value);
                }
                break;

            case MSBuildValueKind.Version:
                if (!Version.TryParse(value, out _))
                {
                    AddErrorWithArgs(CoreDiagnostics.InvalidVersion, value);
                }
                break;

            /*
             * FIXME: these won't work as-is, as inference will add them to the schema
             * case MSBuildValueKind.TargetName:
             *      if (Document.GetSchemas ().GetTarget (value) == null) {
             *              AddErrorWithArgs (CoreDiagnostics.UndefinedTarget, value);
             *      }
             *      break;
             * case MSBuildValueKind.PropertyName:
             *      if (Document.GetSchemas ().GetProperty (value) == null) {
             *              AddErrorWithArgs (CoreDiagnostics.UnknownProperty, value);
             *      }
             *      break;
             * case MSBuildValueKind.ItemName:
             *      if (Document.GetSchemas ().GetItem (value) == null) {
             *              AddErrorWithArgs (CoreDiagnostics.UnknownProperty, value);
             *      }
             *      break;
             */
            case MSBuildValueKind.Lcid:
                if (int.TryParse(value, out int lcid) && lcid > 0)
                {
                    try {
                        CultureInfo.GetCultureInfo(lcid);
                    } catch (CultureNotFoundException) {
                        AddErrorWithArgs(CoreDiagnostics.UnknownLcid, value);
                    }
                }
                else
                {
                    AddErrorWithArgs(CoreDiagnostics.InvalidLcid, value);
                }
                break;

            case MSBuildValueKind.TargetFramework:
                if (!FrameworkInfoProvider.Instance.IsFrameworkShortNameValid(value))
                {
                    AddErrorWithArgs(CoreDiagnostics.UnknownTargetFramework, value);
                }
                break;

            case MSBuildValueKind.TargetFrameworkIdentifier:
                if (!FrameworkInfoProvider.Instance.IsFrameworkIdentifierValid(value))
                {
                    AddErrorWithArgs(CoreDiagnostics.UnknownTargetFrameworkIdentifier, value);
                }
                break;

            case MSBuildValueKind.TargetFrameworkVersion: {
                if (!Version.TryParse(value.TrimStart('v', 'V'), out Version fxv))
                {
                    AddErrorWithArgs(CoreDiagnostics.InvalidVersion, value);
                    break;
                }
                fxv = new Version(Math.Max(fxv.Major, 0), Math.Max(fxv.Minor, 0), Math.Max(fxv.Revision, 0), Math.Max(fxv.Build, 0));
                if (Document is MSBuildRootDocument d && d.Frameworks.Count > 0)
                {
                    bool foundMatch = false;
                    foreach (var fx in d.Frameworks)
                    {
                        if (FrameworkInfoProvider.AreVersionsEquivalent(fx.Version, fxv) && FrameworkInfoProvider.Instance.IsFrameworkVersionValid(fx.Framework, fxv))
                        {
                            foundMatch = true;
                        }
                    }
                    if (!foundMatch)
                    {
                        AddErrorWithArgs(CoreDiagnostics.UnknownTargetFrameworkVersion, value, d.Frameworks[0].Framework);
                    }
                }
                break;
            }

            case MSBuildValueKind.TargetFrameworkProfile: {
                if (Document is MSBuildRootDocument d && d.Frameworks.Count > 0)
                {
                    bool foundMatch = false;
                    foreach (var fx in d.Frameworks)
                    {
                        if (fx.Profile == value && FrameworkInfoProvider.Instance.IsFrameworkProfileValid(fx.Framework, fx.Version, value))
                        {
                            foundMatch = true;
                        }
                    }
                    if (!foundMatch)
                    {
                        AddErrorWithArgs(CoreDiagnostics.UnknownTargetFrameworkProfile, value, d.Frameworks[0].Framework, d.Frameworks[0].Version);
                    }
                }
                break;
            }
            }

            void AddError(MSBuildDiagnosticDescriptor d) => Document.Diagnostics.Add(d, new TextSpan(offset, value.Length));
            void AddErrorWithArgs(MSBuildDiagnosticDescriptor d, params object[] args) => Document.Diagnostics.Add(d, new TextSpan(offset, value.Length), args);
        }
Ejemplo n.º 7
0
        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);
        }
        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);
        }