示例#1
0
 public ResourceMetadataParent(ResourceMetadata metadata, SyntaxBase?indexExpression, bool isNested)
 {
     // TODO: turn this into a record when the target framework supports it
     Metadata        = metadata;
     IndexExpression = indexExpression;
     IsNested        = isNested;
 }
        public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            if (this.deployTimeConstantScopeSyntax is null)
            {
                return;
            }

            if (this.model.GetSymbolInfo(syntax) is VariableSymbol variableSymbol &&
                this.model.Binder.TryGetCycle(variableSymbol) is null)
            {
                // emit any error that has already been triggered previously in the value assignment
                if (this.errorSyntax != null)
                {
                    this.AppendError();
                }
                var variableVisitor = new DeployTimeConstantVariableVisitor(this.model);
                variableVisitor.Visit(variableSymbol.DeclaringSyntax);
                if (variableVisitor.InvalidReferencedBodyType != null)
                {
                    this.errorSyntax          = syntax;
                    this.referencedBodyType   = variableVisitor.InvalidReferencedBodyType;
                    this.variableVisitorStack = variableVisitor.VisitedStack;
                    this.referencedSymbol     = variableVisitor.VisitedStack.Peek();
                }
            }
        }
示例#3
0
 public void EmitOptionalPropertyExpression(string name, SyntaxBase?expression)
 {
     if (expression != null)
     {
         EmitPropertyExpression(name, expression);
     }
 }
示例#4
0
        public override void VisitResourceDeclarationSyntax(ResourceDeclarationSyntax syntax)
        {
            // This check is separate from IsLoopAllowedHere because this is about the appearance of a
            // nested resource **inside** a loop.
            if (this.semanticModel.Binder.GetNearestAncestor <ForSyntax>(syntax) is ForSyntax)
            {
                this.diagnosticWriter.Write(DiagnosticBuilder.ForPosition(syntax.Span).NestedResourceNotAllowedInLoop());
            }

            // Resources can be nested, support recursion of resource declarations
            var previousLoopCapableTopLevelDeclaration = this.activeLoopCapableTopLevelDeclaration;

            this.activeLoopCapableTopLevelDeclaration = syntax;

            // stash the body (handles loops and conditions as well)
            var previousDependsOnProperty = this.currentDependsOnProperty;

            this.currentDependsOnProperty = TryGetDependsOnProperty(syntax.TryGetBody());

            base.VisitResourceDeclarationSyntax(syntax);

            // restore state
            this.currentDependsOnProperty             = previousDependsOnProperty;
            this.activeLoopCapableTopLevelDeclaration = previousLoopCapableTopLevelDeclaration;
        }
示例#5
0
 public DocumentBlockContext(SyntaxBase?openSyntax, SyntaxBase?closeSyntax, SyntaxBase?firstNewLine, SyntaxBase?lastNewLine)
 {
     this.OpenSyntax   = openSyntax;
     this.CloseSyntax  = closeSyntax;
     this.FirstNewLine = firstNewLine;
     this.LastNewLine  = lastNewLine;
 }
        private void AppendError()
        {
            if (this.errorSyntax == null)
            {
                throw new NullReferenceException($"{nameof(this.errorSyntax)} is null in {this.GetType().Name}");
            }
            if (this.currentProperty == null)
            {
                throw new NullReferenceException($"{nameof(this.currentProperty)} is null in {this.GetType().Name} for syntax {this.errorSyntax.ToString()}");
            }
            if (this.bodyObj == null)
            {
                throw new NullReferenceException($"{nameof(this.bodyObj)} is null in {this.GetType().Name} for syntax {this.errorSyntax.ToString()}");
            }
            if (this.referencedBodyObj == null)
            {
                throw new NullReferenceException($"{nameof(this.referencedBodyObj)} is null in {this.GetType().Name} for syntax {this.errorSyntax.ToString()}");
            }
            if (this.accessedSymbol == null)
            {
                throw new NullReferenceException($"{nameof(this.accessedSymbol)} is null in {this.GetType().Name} for syntax {this.errorSyntax.ToString()}");
            }
            var usableKeys = this.referencedBodyObj.Properties.Where(kv => kv.Value.Flags.HasFlag(TypePropertyFlags.DeployTimeConstant)).Select(kv => kv.Key);

            this.diagnosticWriter.Write(DiagnosticBuilder.ForPosition(this.errorSyntax).RuntimePropertyNotAllowed(this.currentProperty, usableKeys, this.accessedSymbol, this.variableVisitorStack?.ToArray().Reverse()));

            this.errorSyntax          = null;
            this.referencedBodyObj    = null;
            this.accessedSymbol       = null;
            this.variableVisitorStack = null;
        }
        public override void VisitObjectSyntax(ObjectSyntax syntax)
        {
            if (this.bodyType == null)
            {
                return;
            }

            if (syntax.HasParseErrors())
            {
                return;
            }

            // Only visit the object properties if they are required to be deploy time constant.
            foreach (var(propertyName, propertySyntax) in syntax.ToNamedPropertyDictionary())
            {
                if (this.bodyType.Properties.TryGetValue(propertyName, out var propertyType) &&
                    propertyType.Flags.HasFlag(TypePropertyFlags.DeployTimeConstant))
                {
                    this.deployTimeConstantScopeSyntax = propertySyntax;
                }

                if (this.deployTimeConstantScopeSyntax is not null)
                {
                    // Reset deployTimeConstantScopeSyntax for nested deploy-time constant properties such as "tags.*".
                    this.deployTimeConstantScopeSyntax = propertySyntax;
                    this.VisitObjectPropertySyntax(propertySyntax);
                }

                if (propertyType is not null &&
                    propertyType.Flags.HasFlag(TypePropertyFlags.DeployTimeConstant))
                {
                    this.deployTimeConstantScopeSyntax = null;
                }
            }
        }
示例#8
0
 public BicepCompletionContext(
     BicepCompletionContextKind kind,
     Range replacementRange,
     SyntaxBase?enclosingDeclaration,
     ObjectSyntax? @object,
     ObjectPropertySyntax?property,
     ArraySyntax?array,
     PropertyAccessSyntax?propertyAccess,
     ResourceAccessSyntax?resourceAccess,
     ArrayAccessSyntax?arrayAccess,
     TargetScopeSyntax?targetScope,
     ImmutableArray <ILanguageScope> activeScopes)
 {
     this.Kind                 = kind;
     this.ReplacementRange     = replacementRange;
     this.EnclosingDeclaration = enclosingDeclaration;
     this.Object               = @object;
     this.Property             = property;
     this.Array                = array;
     this.PropertyAccess       = propertyAccess;
     this.ResourceAccess       = resourceAccess;
     this.ArrayAccess          = arrayAccess;
     this.TargetScope          = targetScope;
     this.ActiveScopes         = activeScopes;
 }
示例#9
0
        private void VisitCommaAndNewLineSeparated(ImmutableArray <SyntaxBase> nodes, bool leadingAndTrailingSpace)
        {
            SyntaxBase?leadingNewLine = null;

            if (nodes.Length > 0 && nodes[0] is Token {
                Type : TokenType.NewLine
            })
示例#10
0
        public FunctionExpression GetReferenceExpression(ResourceMetadata resource, SyntaxBase?indexExpression, bool full)
        {
            var referenceExpression = context.Settings.EnableSymbolicNames ?
                                      GenerateSymbolicReference(resource.Symbol.Name, indexExpression) :
                                      GetFullyQualifiedResourceId(resource);

            // full gives access to top-level resource properties, but generates a longer statement
            if (full)
            {
                return(CreateFunction(
                           "reference",
                           referenceExpression,
                           new JTokenExpression(resource.TypeReference.ApiVersion),
                           new JTokenExpression("full")));
            }

            if (resource.IsExistingResource && !context.Settings.EnableSymbolicNames)
            {
                // we must include an API version for an existing resource, because it cannot be inferred from any deployed template resource
                return(CreateFunction(
                           "reference",
                           referenceExpression,
                           new JTokenExpression(resource.TypeReference.ApiVersion)));
            }

            return(CreateFunction(
                       "reference",
                       referenceExpression));
        }
 public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax)
 {
     base.VisitPropertyAccessSyntax(syntax);
     if (this.errorSyntax != null && TextSpan.AreOverlapping(this.errorSyntax, syntax))
     {
         // Due to the nature of visitPropertyAccessSyntax, we have to propagate the
         // nested errorSyntax up the stack.
         this.errorSyntax = syntax;
     }
     else if (syntax.BaseExpression is VariableAccessSyntax variableAccessSyntax)
     {
         // This is a non-overlapping error, which means that there's two or more runtime properties being referenced
         if (this.errorSyntax != null)
         {
             this.AppendError();
         }
         if (ExtractResourceOrModuleSymbolAndBodyObj(this.model, variableAccessSyntax) is ({ } declaredSymbol, { } referencedBodyObj) &&
             referencedBodyObj.Properties.TryGetValue(syntax.PropertyName.IdentifierName, out var propertyType) &&
             !propertyType.Flags.HasFlag(TypePropertyFlags.DeployTimeConstant))
         {
             this.errorSyntax       = syntax;
             this.accessedSymbol    = declaredSymbol.Name;
             this.referencedBodyObj = referencedBodyObj;
         }
     }
 }
示例#12
0
        public FunctionExpression GetReferenceExpression(ResourceMetadata resource, SyntaxBase?indexExpression, bool full)
        {
            var referenceExpression = context.Settings.EnableSymbolicNames ?
                                      GenerateSymbolicReference(resource.Symbol.Name, indexExpression) :
                                      GetFullyQualifiedResourceId(resource);

            // full gives access to top-level resource properties, but generates a longer statement
            if (full)
            {
                var apiVersion = resource.TypeReference.ApiVersion ?? throw new InvalidOperationException($"Expected resource type {resource.TypeReference.FormatName()} to contain version");
                return(CreateFunction(
                           "reference",
                           referenceExpression,
                           new JTokenExpression(apiVersion),
                           new JTokenExpression("full")));
            }

            // we've got a reference to a resource which isn't explicitly defined in this template - we must supply the apiVersion for reference() to work
            if (resource.IsExistingResource && !context.Settings.EnableSymbolicNames)
            {
                var apiVersion = resource.TypeReference.ApiVersion ?? throw new InvalidOperationException($"Expected resource type {resource.TypeReference.FormatName()} to contain version");
                return(CreateFunction(
                           "reference",
                           referenceExpression,
                           new JTokenExpression(apiVersion)));
            }

            return(CreateFunction(
                       "reference",
                       referenceExpression));
        }
示例#13
0
        private static ScopeData?ValidateScope(SemanticModel semanticModel, LogInvalidScopeDiagnostic logInvalidScopeFunc, ResourceScope supportedScopes, SyntaxBase bodySyntax, SyntaxBase?scopeValue)
        {
            if (scopeValue is null)
            {
                // no scope provided - use the target scope for the file
                if (!supportedScopes.HasFlag(semanticModel.TargetScope))
                {
                    logInvalidScopeFunc(bodySyntax, semanticModel.TargetScope, supportedScopes);
                    return(null);
                }

                return(null);
            }

            var(scopeSymbol, indexExpression) = scopeValue switch
            {
                // scope indexing can only happen with references to module or resource collections
                ArrayAccessSyntax {
                    BaseExpression : VariableAccessSyntax baseVariableAccess
                } arrayAccess => (semanticModel.GetSymbolInfo(baseVariableAccess), arrayAccess.IndexExpression),
                ArrayAccessSyntax {
                    BaseExpression : ResourceAccessSyntax baseVariableAccess
                } arrayAccess => (semanticModel.GetSymbolInfo(baseVariableAccess), arrayAccess.IndexExpression),

                // all other scope expressions
                _ => (semanticModel.GetSymbolInfo(scopeValue), null)
            };

            var scopeType = semanticModel.GetTypeInfo(scopeValue);

            switch (scopeType)
            {
            case TenantScopeType type:
                if (!supportedScopes.HasFlag(ResourceScope.Tenant))
                {
                    logInvalidScopeFunc(scopeValue, ResourceScope.Tenant, supportedScopes);
                    return(null);
                }

                return(new ScopeData {
                    RequestedScope = ResourceScope.Tenant, IndexExpression = indexExpression
                });

            case ManagementGroupScopeType type:
                if (!supportedScopes.HasFlag(ResourceScope.ManagementGroup))
                {
                    logInvalidScopeFunc(scopeValue, ResourceScope.ManagementGroup, supportedScopes);
                    return(null);
                }

                return(type.Arguments.Length switch
                {
                    0 => new ScopeData {
                        RequestedScope = ResourceScope.ManagementGroup, IndexExpression = indexExpression
                    },
                    _ => new ScopeData {
                        RequestedScope = ResourceScope.ManagementGroup, ManagementGroupNameProperty = type.Arguments[0].Expression, IndexExpression = indexExpression
                    },
                });
示例#14
0
 public BicepCompletionContext(BicepCompletionContextKind kind, SyntaxBase?enclosingDeclaration = null, ObjectSyntax? @object = null, ObjectPropertySyntax?property = null, ArraySyntax?array = null)
 {
     this.Kind = kind;
     this.EnclosingDeclaration = enclosingDeclaration;
     this.Object   = @object;
     this.Property = property;
     this.Array    = array;
 }
示例#15
0
        public ResourceTypeSyntax(Token keyword, SyntaxBase?type)
        {
            AssertKeyword(keyword, nameof(keyword), LanguageConstants.ResourceKeyword);
            AssertSyntaxType(type, nameof(type), typeof(StringSyntax), typeof(SkippedTriviaSyntax));

            this.Keyword = keyword;
            this.Type    = type;
        }
示例#16
0
        public void EmitUnqualifiedResourceId(ResourceSymbol resourceSymbol, SyntaxBase?indexExpression, SyntaxBase newContext)
        {
            var converterForContext = converter.CreateConverterForIndexReplacement(ExpressionConverter.GetResourceNameSyntax(resourceSymbol), indexExpression, newContext);

            var unqualifiedResourceId = converterForContext.GetUnqualifiedResourceId(resourceSymbol);
            var serialized            = ExpressionSerializer.SerializeExpression(unqualifiedResourceId);

            writer.WriteValue(serialized);
        }
示例#17
0
        public void Visit(SyntaxBase?node)
        {
            if (node == null)
            {
                return;
            }

            VisitInternal(node);
        }
示例#18
0
        public void EmitResourceIdReference(ModuleSymbol moduleSymbol, SyntaxBase?indexExpression, SyntaxBase newContext)
        {
            var converterForContext = this.converter.CreateConverterForIndexReplacement(ExpressionConverter.GetModuleNameSyntax(moduleSymbol), indexExpression, newContext);

            var resourceIdExpression = converterForContext.GetFullyQualifiedResourceId(moduleSymbol);
            var serialized           = ExpressionSerializer.SerializeExpression(resourceIdExpression);

            writer.WriteValue(serialized);
        }
示例#19
0
        private void ReplaceCurrent <TSyntax>(TSyntax syntax, Func <TSyntax, SyntaxBase> replaceFunc)
            where TSyntax : SyntaxBase
        {
            if (currentSyntax is not null)
            {
                throw new InvalidOperationException($"Expected {nameof(currentSyntax)} to be null");
            }

            currentSyntax = replaceFunc(syntax);
        }
示例#20
0
        public void EmitCopyObject(string?name, ForSyntax syntax, SyntaxBase?input, string?copyIndexOverride = null)
        {
            writer.WriteStartObject();

            if (name is not null)
            {
                this.EmitProperty("name", name);
            }

            // construct the length ARM expression from the Bicep array expression
            // type check has already ensured that the array expression is an array
            this.EmitPropertyWithTransform(
                "count",
                syntax.Expression,
                arrayExpression => new FunctionExpression("length", new[] { arrayExpression }, Array.Empty <LanguageExpression>()));

            if (input != null)
            {
                if (copyIndexOverride == null)
                {
                    this.EmitProperty("input", input);
                }
                else
                {
                    this.EmitPropertyWithTransform("input", input, expression =>
                    {
                        // the named copy index in the serialized expression is incorrect
                        // because the object syntax here does not match the JSON equivalent due to the presence of { "value": ... } wrappers
                        // for now, we will manually replace the copy index in the converted expression
                        // this approach will not work for nested property loops
                        var visitor = new LanguageExpressionVisitor
                        {
                            OnFunctionExpression = function =>
                            {
                                if (string.Equals(function.Function, "copyIndex") &&
                                    function.Parameters.Length == 1 &&
                                    function.Parameters[0] is JTokenExpression)
                                {
                                    // it's an invocation of the copyIndex function with 1 argument with a literal value
                                    // replace the argument with the correct value
                                    function.Parameters = new LanguageExpression[] { new JTokenExpression("value") };
                                }
                            }
                        };

                        // mutate the expression
                        expression.Accept(visitor);

                        return(expression);
                    });
                }
            }

            writer.WriteEndObject();
        }
示例#21
0
        public void Visit(SyntaxBase?node)
        {
            if (node == null)
            {
                return;
            }

            RuntimeHelpers.EnsureSufficientExecutionStack();

            VisitInternal(node);
        }
示例#22
0
        protected virtual SyntaxBase RewriteInternal(SyntaxBase syntax)
        {
            currentSyntax = null;
            syntax.Accept(this);

            if (currentSyntax is null)
            {
                throw new InvalidOperationException($"Expected {nameof(currentSyntax)} to not be null");
            }

            return(currentSyntax);
        }
示例#23
0
            protected override void VisitInternal(SyntaxBase syntax)
            {
                syntaxList.Add(new(Syntax: syntax, Parent: parent, Depth: depth));

                var prevParent = parent;

                parent = syntax;
                depth++;
                base.VisitInternal(syntax);
                depth--;
                parent = prevParent;
            }
示例#24
0
        public override void VisitModuleDeclarationSyntax(ModuleDeclarationSyntax syntax)
        {
            this.activeLoopCapableTopLevelDeclaration = syntax;

            // stash the body (handles loops and conditions as well)
            this.currentDependsOnProperty = TryGetDependsOnProperty(syntax.TryGetBody());

            base.VisitModuleDeclarationSyntax(syntax);

            // clear the stash
            this.currentDependsOnProperty = null;

            this.activeLoopCapableTopLevelDeclaration = null;
        }
示例#25
0
        protected static void AssertSyntaxType(SyntaxBase?syntax, [InvokerParameterName] string parameterName, params Type[] expectedTypes)
        {
            if (syntax == null)
            {
                return;
            }

            var syntaxType = syntax.GetType();

            if (expectedTypes.Any(expectedType => syntaxType == expectedType) == false)
            {
                throw new ArgumentException($"{parameterName} is of an unexpected type {syntaxType.Name}. Expected types: {expectedTypes.Select(t => t.Name).ConcatString(", ")}");
            }
        }
示例#26
0
        private bool Rewrite <TSyntax>(TSyntax syntax, out TSyntax newSyntax)
            where TSyntax : SyntaxBase
        {
            currentSyntax = null;
            syntax.Accept(this);

            if (currentSyntax is not TSyntax rewrittenSyntax)
            {
                throw new InvalidOperationException($"Expected {nameof(currentSyntax)} to be of type {typeof(TSyntax)}");
            }

            newSyntax = rewrittenSyntax;
            return(!object.ReferenceEquals(newSyntax, syntax));
        }
        public override void VisitIfConditionSyntax(IfConditionSyntax syntax)
        {
            this.Visit(syntax.Keyword);
            this.deployTimeConstantScopeSyntax = syntax;
            this.Visit(syntax.ConditionExpression);

            if (this.errorSyntax != null)
            {
                this.AppendError();
            }

            this.deployTimeConstantScopeSyntax = null;
            this.Visit(syntax.Body);
        }
示例#28
0
 public ResourceMetadata(
     ResourceType type,
     SyntaxBase nameSyntax,
     ResourceSymbol symbol,
     ResourceMetadataParent?parent,
     SyntaxBase?scopeSyntax,
     bool isExistingResource)
 {
     // TODO: turn this into a record when the target framework supports it
     Type               = type;
     NameSyntax         = nameSyntax;
     Symbol             = symbol;
     Parent             = parent;
     ScopeSyntax        = scopeSyntax;
     IsExistingResource = isExistingResource;
 }
        public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax)
        {
            base.VisitPropertyAccessSyntax(syntax);
            if (this.errorSyntax != null && TextSpan.AreOverlapping(this.errorSyntax, syntax))
            {
                // Due to the nature of visitPropertyAccessSyntax, we have to propagate the
                // nested errorSyntax up the stack.
                this.errorSyntax = syntax;
            }
            else
            {
                switch (syntax.BaseExpression)
                {
                case VariableAccessSyntax variableAccessSyntax:
                {
                    // This is a non-overlapping error, which means that there's two or more runtime properties being referenced
                    if (this.errorSyntax != null)
                    {
                        this.AppendError();
                    }

                    if (ExtractResourceOrModuleSymbolAndBodyType(this.model, variableAccessSyntax) is ({ } referencedSymbol, { } referencedBodyType))
                    {
                        SetState(syntax, referencedSymbol, referencedBodyType, syntax.PropertyName.IdentifierName);
                    }

                    break;
                }

                case ArrayAccessSyntax {
                        BaseExpression: VariableAccessSyntax baseVariableAccess
                } :
                    {
                        if (this.errorSyntax != null)
                        {
                            this.AppendError();
                        }

                        if (ExtractResourceOrModuleCollectionSymbolAndBodyType(this.model, baseVariableAccess) is ({ } referencedSymbol, { } referencedBodyType))
                        {
                            SetState(syntax, referencedSymbol, referencedBodyType, syntax.PropertyName.IdentifierName);
                        }

                        break;
                    }
                }
            }
示例#30
0
        public static bool IsAddingPropertyLoopAllowed(SemanticModel semanticModel, ObjectPropertySyntax property)
        {
            SyntaxBase?current           = property;
            int        propertyLoopCount = 0;

            while (current is not null)
            {
                var parent = semanticModel.SourceFile.Hierarchy.GetParent(current);
                if (current is ForSyntax @for && IsPropertyLoop(parent, @for))
                {
                    ++propertyLoopCount;
                }

                current = parent;
            }

            // adding a new property loop is only allowed if we're under the limit
            return(propertyLoopCount < MaximumNestedPropertyLoopCount);
        }