Ejemplo n.º 1
0
        protected override SyntaxBase ReplaceStringSyntax(StringSyntax syntax)
        {
            var declaredType = semanticModel.GetDeclaredType(syntax);

            if (semanticModel.GetTypeInfo(syntax) is not StringLiteralType actualType)
            {
                return(base.ReplaceStringSyntax(syntax));
            }

            if (declaredType is null || TypeValidator.AreTypesAssignable(actualType, declaredType))
            {
                return(base.ReplaceStringSyntax(syntax));
            }

            var stringLiteralCandidates = Enumerable.Empty <StringLiteralType>();

            if (declaredType is StringLiteralType stringLiteralType)
            {
                stringLiteralCandidates = stringLiteralType.AsEnumerable();
            }
            else if (declaredType is UnionType unionType && unionType.Members.All(x => x.Type is StringLiteralType))
            {
                stringLiteralCandidates = unionType.Members.Select(x => (StringLiteralType)x.Type);
            }

            var insensitiveMatch = stringLiteralCandidates.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Equals(x.Name, actualType.Name));

            if (insensitiveMatch == null)
            {
                return(base.ReplaceStringSyntax(syntax));
            }

            return(SyntaxFactory.CreateStringLiteral(insensitiveMatch.RawStringValue));
        }
Ejemplo n.º 2
0
        public override void VisitStringSyntax(StringSyntax syntax)
        => AssignType(syntax, () => {
            if (syntax.TryGetLiteralValue() is string literalValue)
            {
                // uninterpolated strings have a known type
                return(new StringLiteralType(literalValue));
            }

            var errors = new List <ErrorDiagnostic>();

            foreach (var interpolatedExpression in syntax.Expressions)
            {
                var expressionType = typeManager.GetTypeInfo(interpolatedExpression);
                CollectErrors(errors, expressionType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // normally we would also do an assignability check, but we allow "any" type in string interpolation expressions
            // so the assignability check cannot possibly fail (we already collected type errors from the inner expressions at this point)
            return(LanguageConstants.String);
        });
Ejemplo n.º 3
0
        private TypeSymbol GetStringType(TypeManagerContext context, StringSyntax @string)
        {
            if (@string.IsInterpolated() == false)
            {
                // uninterpolated strings have a known type
                return(LanguageConstants.String);
            }

            var errors = new List <ErrorDiagnostic>();

            foreach (var interpolatedExpression in @string.Expressions)
            {
                var expressionType = this.GetTypeInfoInternal(context, interpolatedExpression);
                CollectErrors(errors, expressionType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // normally we would also do an assignability check, but we allow "any" type in string interpolation expressions
            // so the assignability check cannot possibly fail (we already collected type errors from the inner expressions at this point)
            return(LanguageConstants.String);
        }
Ejemplo n.º 4
0
 public ResourceDefinition(string resourceName, ResourceSymbol?resourceScope, string resourceTypeFQDN, StringSyntax resourceNamePropertyValue)
 {
     ResourceName              = resourceName;
     ResourceScope             = resourceScope;
     ResourceTypeFQDN          = resourceTypeFQDN;
     ResourceNamePropertyValue = resourceNamePropertyValue;
 }
Ejemplo n.º 5
0
 public ResourceDefinition(string resourceName, ResourceMetadata?resourceScope, string fullyQualifiedResourceType, StringSyntax resourceNamePropertyValue)
 {
     ResourceName  = resourceName;
     ResourceScope = resourceScope;
     FullyQualifiedResourceType = fullyQualifiedResourceType;
     ResourceNamePropertyValue  = resourceNamePropertyValue;
 }
Ejemplo n.º 6
0
 public ModuleDefinition(string moduleName, ResourceScope modulePropertyScopeType, ImmutableArray <StringSyntax?>?modulePropertyScopeValue, StringSyntax modulePropertyNameValue)
 {
     ModuleName = moduleName;
     ModulePropertyScopeType  = modulePropertyScopeType;
     ModulePropertyScopeValue = modulePropertyScopeValue;
     ModulePropertyNameValue  = modulePropertyNameValue;
 }
Ejemplo n.º 7
0
        private LanguageExpression ConvertString(StringSyntax syntax)
        {
            if (syntax.TryGetLiteralValue() is string literalStringValue)
            {
                // no need to build a format string
                return(new JTokenExpression(literalStringValue));;
            }

            if (syntax.Expressions.Length == 1)
            {
                const string emptyStringOpen  = LanguageConstants.StringDelimiter + LanguageConstants.StringHoleOpen;  // '${
                const string emptyStringClose = LanguageConstants.StringHoleClose + LanguageConstants.StringDelimiter; // }'

                // Special-case interpolation of format '${myValue}' because it's a common pattern for userAssignedIdentities.
                // There's no need for a 'format' function because we just have a single expression with no outer formatting.
                if (syntax.StringTokens[0].Text == emptyStringOpen && syntax.StringTokens[1].Text == emptyStringClose)
                {
                    return(ConvertExpression(syntax.Expressions[0]));
                }
            }

            var formatArgs = new LanguageExpression[syntax.Expressions.Length + 1];

            var formatString = StringFormatConverter.BuildFormatString(syntax);

            formatArgs[0] = new JTokenExpression(formatString);

            for (var i = 0; i < syntax.Expressions.Length; i++)
            {
                formatArgs[i + 1] = ConvertExpression(syntax.Expressions[i]);
            }

            return(CreateFunction("format", formatArgs));
        }
Ejemplo n.º 8
0
        public void Render(StringSyntax @string)
        {
            Render(@string.StartStringSymbol);
            if (@string.StringNode != null)
            {
                Render(@string.StringNode);
            }

            Render(@string.EndStringSymbol);
        }
        public SyntaxBase?TryGetReplacementStringSyntax(StringSyntax parent, StringSyntax child, ResourceSymbol parentResourceSymbol)
        {
            if (parent.SegmentValues.Length > child.SegmentValues.Length ||
                parent.Expressions.Length > child.Expressions.Length)
            {
                return(null);
            }

            for (var i = 0; i < parent.Expressions.Length; i++)
            {
                var childSymbol  = semanticModel.GetSymbolInfo(child.Expressions[i]);
                var parentSymbol = semanticModel.GetSymbolInfo(parent.Expressions[i]);

                if (childSymbol == null || childSymbol != parentSymbol)
                {
                    return(null);
                }
            }

            for (var i = 0; i < parent.SegmentValues.Length - 1; i++)
            {
                if (child.SegmentValues[i] != parent.SegmentValues[i])
                {
                    return(null);
                }
            }

            var finalIndex = parent.SegmentValues.Length - 1;

            if (!child.SegmentValues[finalIndex].StartsWith(parent.SegmentValues[finalIndex], StringComparison.Ordinal))
            {
                return(null);
            }

            var finalSegmentSuffix = child.SegmentValues[finalIndex].Substring(parent.SegmentValues[finalIndex].Length);

            if (finalSegmentSuffix.Length == 0 || finalSegmentSuffix[0] != '/')
            {
                return(null);
            }

            var newNameValues = new [] { finalSegmentSuffix.Substring(1) }.Concat(child.SegmentValues.Skip(finalIndex + 1)).ToArray();
            var newExpressions = child.Expressions.Skip(finalIndex).ToArray();

            if (newNameValues.Length == 2 && newNameValues[0] == "" && newNameValues[1] == "")
            {
                // return "expr" rather than "'${expr}'"
                return(newExpressions[0]);
            }

            return(SyntaxFactory.CreateString(newNameValues, newExpressions));
        }
Ejemplo n.º 10
0
        protected override SyntaxBase ReplaceObjectSyntax(ObjectSyntax syntax)
        {
            var declaredType = semanticModel.GetDeclaredType(syntax);

            if (declaredType is not ObjectType objectType)
            {
                return(base.ReplaceObjectSyntax(syntax));
            }

            var newChildren = new List <SyntaxBase>();

            foreach (var child in syntax.Children)
            {
                if (child is ObjectPropertySyntax objectProperty &&
                    objectProperty.TryGetKeyText() is string propertyKey &&
                    !objectType.Properties.ContainsKey(propertyKey))
                {
                    var insensitivePropertyKey = objectType.Properties.Keys.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Equals(x, propertyKey));
                    if (insensitivePropertyKey != null)
                    {
                        SyntaxBase newKeySyntax;
                        if (Regex.IsMatch(insensitivePropertyKey, "^[a-zA-Z][a-zA-Z0-9_]*$"))
                        {
                            newKeySyntax = new IdentifierSyntax(new Token(TokenType.Identifier, new TextSpan(0, 0), insensitivePropertyKey, Enumerable.Empty <SyntaxTrivia>(), Enumerable.Empty <SyntaxTrivia>()));
                        }
                        else
                        {
                            var stringToken = new Token(TokenType.StringComplete, new TextSpan(0, 0), StringUtils.EscapeBicepString(insensitivePropertyKey), Enumerable.Empty <SyntaxTrivia>(), Enumerable.Empty <SyntaxTrivia>());
                            newKeySyntax = new StringSyntax(stringToken.AsEnumerable(), Enumerable.Empty <SyntaxBase>(), insensitivePropertyKey.AsEnumerable());
                        }

                        newChildren.Add(new ObjectPropertySyntax(
                                            newKeySyntax,
                                            objectProperty.Colon,
                                            Rewrite(objectProperty.Value)));
                        continue;
                    }
                }

                newChildren.Add(Rewrite(child));
            }

            if (Enumerable.SequenceEqual(newChildren, syntax.Children))
            {
                return(base.ReplaceObjectSyntax(syntax));
            }

            return(new ObjectSyntax(
                       syntax.OpenBrace,
                       newChildren,
                       syntax.CloseBrace));
        }
            public override void VisitStringSyntax(StringSyntax syntax)
            {
                var disallowedHost = syntax.SegmentValues.Select(s => FindEnvironmentUrlInString(s))
                                     .Where(span => span != null)
                                     .FirstOrDefault();

                if (disallowedHost != null)
                {
                    this.DisallowedHostSpans.Add((disallowedHost, syntax.Span));
                }

                base.VisitStringSyntax(syntax);
            }
Ejemplo n.º 12
0
        private DeclaredTypeAssignment?GetStringType(StringSyntax syntax)
        {
            var parent = this.binder.GetParent(syntax);

            // we are only handling paths in the AST that are going to produce a declared type
            // strings can exist under a variable declaration, but variables don't have declared types,
            // so we don't need to check that case
            if (parent is ObjectPropertySyntax || parent is ArrayItemSyntax)
            {
                // this string is a value of the property
                // the declared type should be the same as the string and we should propagate the flags
                return(GetDeclaredTypeAssignment(parent)?.ReplaceDeclaringSyntax(syntax));
            }

            return(null);
        }
Ejemplo n.º 13
0
        public static string BuildFormatString(StringSyntax syntax)
        {
            var stringBuilder = new StringBuilder();
            var values        = syntax.SegmentValues;

            for (var i = 0; i < values.Length - 1; i++)
            {
                AppendAndEscapeCurlies(stringBuilder, values[i]);
                stringBuilder.Append('{');
                stringBuilder.Append(i);
                stringBuilder.Append('}');
            }

            AppendAndEscapeCurlies(stringBuilder, values[values.Length - 1]);

            return(stringBuilder.ToString());
        }
Ejemplo n.º 14
0
        private LanguageExpression ConvertString(StringSyntax syntax)
        {
            if (!syntax.IsInterpolated())
            {
                // no need to build a format string
                return(new JTokenExpression(syntax.GetLiteralValue()));;
            }

            var formatArgs = new LanguageExpression[syntax.Expressions.Length + 1];

            var formatString = StringFormatConverter.BuildFormatString(syntax);

            formatArgs[0] = new JTokenExpression(formatString);

            for (var i = 0; i < syntax.Expressions.Length; i++)
            {
                formatArgs[i + 1] = ConvertExpression(syntax.Expressions[i]);
            }

            return(new FunctionExpression("format", formatArgs, Array.Empty <LanguageExpression>()));
        }
        public SyntaxBase?TryGetReplacementChildName(StringSyntax childName, SyntaxBase parentName, ResourceSymbol parentResourceSymbol)
        {
            switch (parentName)
            {
            case VariableAccessSyntax parentVarAccess:
            {
                if (childName.Expressions.FirstOrDefault() is not VariableAccessSyntax childVarAccess ||
                    semanticModel.GetSymbolInfo(parentVarAccess) != semanticModel.GetSymbolInfo(childVarAccess))
                {
                    return(null);
                }

                if (!childName.SegmentValues[1].StartsWith("/"))
                {
                    return(null);
                }

                var newName = SyntaxFactory.CreateString(
                    new [] { childName.SegmentValues[1].Substring(1) }.Concat(childName.SegmentValues.Skip(2)),
                    childName.Expressions.Skip(1));

                return(newName);
            }

            case StringSyntax parentString:
            {
                if (TryGetReplacementStringSyntax(parentString, childName, parentResourceSymbol) is not {
                    } newName)
                {
                    return(null);
                }

                return(newName);
            }
            }

            return(null);
        }
            public override void VisitStringSyntax(StringSyntax syntax)
            {
                foreach (var segment in syntax.SegmentValues)
                {
                    var exclusionMatches = exclusionRegex.Matches(segment);

                    // does this segment have a host match
                    foreach (Match match in this.hostRegex.Matches(segment))
                    {
                        // exclusion is found containing the host match
                        var isExcluded = exclusionMatches.Any(exclusionMatch =>
                                                              match.Index > exclusionMatch.Index &&
                                                              match.Index + match.Length <= exclusionMatch.Index + exclusionMatch.Length);

                        if (!isExcluded)
                        {
                            // create a span for the specific identified instance
                            // to allow for multiple instances in a single syntax
                            this.DisallowedHostSpans[new TextSpan(syntax.Span.Position + match.Index, match.Length)] = match.Value;
                        }
                    }
                    base.VisitStringSyntax(syntax);
                }
            }
Ejemplo n.º 17
0
        private FunctionExpression ConvertObject(ObjectSyntax syntax)
        {
            // need keys and values in one array of parameters
            var parameters = new LanguageExpression[syntax.Properties.Count() * 2];

            int index = 0;

            foreach (var propertySyntax in syntax.Properties)
            {
                parameters[index] = propertySyntax.Key switch
                {
                    IdentifierSyntax identifier => new JTokenExpression(identifier.IdentifierName),
                    StringSyntax @string => ConvertString(@string),
                    _ => throw new NotImplementedException($"Encountered an unexpected type '{propertySyntax.Key.GetType().Name}' when generating object's property name.")
                };
                index++;

                parameters[index] = ConvertExpression(propertySyntax.Value);
                index++;
            }

            // we are using the createObject() function as a proxy for an object literal
            return(GetCreateObjectExpression(parameters));
        }
Ejemplo n.º 18
0
        protected override SyntaxBase ReplaceResourceDeclarationSyntax(ResourceDeclarationSyntax syntax)
        {
            if (syntax.TryGetBody() is not ObjectSyntax resourceBody ||
                resourceBody.SafeGetPropertyByName("name") is not ObjectPropertySyntax resourceNameProp ||
                resourceNameProp.Value is not StringSyntax resourceName)
            {
                return(syntax);
            }

            if (semanticModel.GetSymbolInfo(syntax) is not ResourceSymbol resourceSymbol ||
                resourceSymbol.Type is not ResourceType resourceType)
            {
                return(syntax);
            }

            if (resourceType.TypeReference.Types.Length < 2)
            {
                // we're only looking for child resources here
                return(syntax);
            }

            foreach (var otherResourceSymbol in semanticModel.Root.GetAllResourceDeclarations())
            {
                if (otherResourceSymbol.Type is not ResourceType otherResourceType ||
                    otherResourceType.TypeReference.Types.Length != resourceType.TypeReference.Types.Length - 1 ||
                    !resourceType.TypeReference.TypesString.StartsWith(otherResourceType.TypeReference.TypesString, StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                // The other resource is a parent type to this one. check if we can refactor the name.
                if (otherResourceSymbol.DeclaringResource.TryGetBody() is not ObjectSyntax otherResourceBody ||
                    otherResourceBody.SafeGetPropertyByName("name") is not ObjectPropertySyntax otherResourceNameProp)
                {
                    continue;
                }

                StringSyntax replacementStringSyntax;
                if (otherResourceNameProp.Value is StringSyntax otherResourceName)
                {
                    var newStringSyntax = TryGetReplacementStringSyntax(otherResourceName, resourceName, otherResourceSymbol);
                    if (newStringSyntax == null)
                    {
                        continue;
                    }

                    replacementStringSyntax = newStringSyntax;
                }
                else if (otherResourceNameProp.Value is VariableAccessSyntax parentVarAccess &&
                         resourceName.Expressions.FirstOrDefault() is VariableAccessSyntax childVarAccess)
                {
                    if (semanticModel.GetSymbolInfo(parentVarAccess) != semanticModel.GetSymbolInfo(childVarAccess))
                    {
                        continue;
                    }

                    var otherResourceIdentifier = new Token(TokenType.Identifier, new TextSpan(0, 0), otherResourceSymbol.Name, Enumerable.Empty <SyntaxTrivia>(), Enumerable.Empty <SyntaxTrivia>());
                    var nameProperty            = new Token(TokenType.Identifier, new TextSpan(0, 0), "name", Enumerable.Empty <SyntaxTrivia>(), Enumerable.Empty <SyntaxTrivia>());

                    var replacementExpression = new PropertyAccessSyntax(
                        new VariableAccessSyntax(new IdentifierSyntax(otherResourceIdentifier)),
                        new Token(TokenType.Dot, new TextSpan(0, 0), ".", Enumerable.Empty <SyntaxTrivia>(), Enumerable.Empty <SyntaxTrivia>()),
                        new IdentifierSyntax(nameProperty));

                    replacementStringSyntax = new StringSyntax(
                        resourceName.StringTokens,
                        replacementExpression.AsEnumerable().Concat(resourceName.Expressions.Skip(1)),
                        resourceName.SegmentValues);
                }
Ejemplo n.º 19
0
 /// <summary>
 /// Checks if the syntax node contains an interpolated string or a literal string.
 /// </summary>
 /// <param name="syntax">The string syntax node</param>
 public static bool IsInterpolated(this StringSyntax syntax)
 => syntax.SegmentValues.Length > 1;
Ejemplo n.º 20
0
 /// <summary>
 /// Try to get the string literal value for a syntax node. Returns null if the string is interpolated.
 /// </summary>
 /// <param name="syntax">The string syntax node</param>
 public static string?TryGetLiteralValue(this StringSyntax syntax)
 => syntax.IsInterpolated() ? null : syntax.SegmentValues[0];