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();
                }
            }
        }
 // This method should not be visited by PropertyAccessSyntax of Resource/Modules (But Variables should visit). This is meant to catch variable
 // properties which are assigned entire resource/modules, or to recurse through a chain of variable references.
 public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
 {
     if (DeployTimeConstantVisitor.ExtractResourceOrModuleSymbolAndBodyType(this.model, syntax) is ({} referencedSymbol, {} referencedBodyType))
     {
         this.InvalidReferencedBodyType = referencedBodyType;
         VisitedStack.Push(referencedSymbol);
     }
예제 #3
0
        private TypeSymbol GetVariableAccessType(TypeManagerContext context, VariableAccessSyntax syntax)
        {
            var symbol = this.ResolveSymbol(syntax);

            switch (symbol)
            {
            case ErrorSymbol errorSymbol:
                // variable bind failure - pass the error along
                return(errorSymbol.ToErrorType());

            case ResourceSymbol resource:
                return(GetResourceType(context, syntax, resource));

            case ParameterSymbol parameter:
                return(GetParameterType(context, syntax, parameter));

            case VariableSymbol variable:
                return(HandleSymbolType(syntax.Name.IdentifierName, syntax.Name.Span, variable.GetVariableType(context)));

            case OutputSymbol _:
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Name.Span).OutputReferenceNotSupported(syntax.Name.IdentifierName)));

            default:
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Name.Span).SymbolicNameIsNotAVariableOrParameter(syntax.Name.IdentifierName)));
            }
        }
예제 #4
0
        private void VisitVariableAccessSyntaxInternal(VariableAccessSyntax syntax)
        {
            if (currentDeclaration == null)
            {
                return;
            }

            if (shouldInlineCache[currentDeclaration])
            {
                // we've already made a decision
                return;
            }

            switch (model.GetSymbolInfo(syntax))
            {
            case VariableSymbol variableSymbol:
                if (!shouldInlineCache.TryGetValue(variableSymbol, out var shouldInline))
                {
                    // recursively visit dependent variables
                    this.Visit(variableSymbol.DeclaringSyntax);

                    shouldInline = shouldInlineCache[variableSymbol];
                }

                // if we depend on a variable that requires inlining, then we also require inlining
                shouldInlineCache[currentDeclaration] |= shouldInline;
                return;
            }
        }
예제 #5
0
        private DeclaredTypeAssignment?GetVariableAccessType(VariableAccessSyntax syntax)
        {
            // references to symbols can be involved in cycles
            // we should not try to obtain the declared type for such symbols because we will likely never finish
            bool IsCycleFree(DeclaredSymbol declaredSymbol) => !this.cyclesBySyntax.ContainsKey(declaredSymbol);

            // because all variable access nodes are normally bound to something, this should always return true
            // (if not, the following code handles that gracefully)
            this.bindings.TryGetValue(syntax, out var symbol);

            switch (symbol)
            {
            case ResourceSymbol resourceSymbol when IsCycleFree(resourceSymbol):
                // the declared type of the body is more useful to us than the declared type of the resource itself
                return(this.GetDeclaredTypeAssignment(resourceSymbol.DeclaringResource.Body));

            case ModuleSymbol moduleSymbol when IsCycleFree(moduleSymbol):
                // the declared type of the body is more useful to us than the declared type of the module itself
                return(this.GetDeclaredTypeAssignment(moduleSymbol.DeclaringModule.Body));

            case DeclaredSymbol declaredSymbol when IsCycleFree(declaredSymbol):
                // the syntax node is referencing a declared symbol
                // use its declared type
                return(this.GetDeclaredTypeAssignment(declaredSymbol.DeclaringSyntax));

            case NamespaceSymbol namespaceSymbol:
                // the syntax node is referencing a namespace - use its type
                return(new DeclaredTypeAssignment(namespaceSymbol.Type, declaringSyntax: null));
            }

            return(null);
        }
 // This method should not be visited by PropertyAccessSyntax of Resource/Modules (But Variables should visit). This is meant to catch variable
 // properties which are assigned entire resource/modules, or to recurse through a chain of variable references.
 public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
 {
     if (DeployTimeConstantVisitor.ExtractResourceOrModuleSymbolAndBodyObj(this.model, syntax) is ({} declaredSymbol, {} referencedBodyObj))
     {
         this.invalidReferencedBodyObj = referencedBodyObj;
         visitedStack.Push(declaredSymbol.Name);
     }
예제 #7
0
        private LanguageExpression ConvertVariableAccess(VariableAccessSyntax variableAccessSyntax)
        {
            string name = variableAccessSyntax.Name.IdentifierName;

            var symbol = context.SemanticModel.GetSymbolInfo(variableAccessSyntax);

            // TODO: This will change to support inlined functions like reference() or list*()
            switch (symbol)
            {
            case ParameterSymbol _:
                return(CreateFunction("parameters", new JTokenExpression(name)));

            case VariableSymbol variableSymbol:
                if (context.VariablesToInline.Contains(variableSymbol))
                {
                    // we've got a runtime dependency, so we have to inline the variable usage
                    return(ConvertExpression(variableSymbol.DeclaringVariable.Value));
                }
                return(CreateFunction("variables", new JTokenExpression(name)));

            case ResourceSymbol resourceSymbol:
                var typeReference = EmitHelpers.GetTypeReference(resourceSymbol);
                return(GetReferenceExpression(resourceSymbol, typeReference, true));

            case ModuleSymbol moduleSymbol:
                return(GetModuleOutputsReferenceExpression(moduleSymbol));

            default:
                throw new NotImplementedException($"Encountered an unexpected symbol kind '{symbol?.Kind}' when generating a variable access expression.");
            }
        }
        public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            var symbol = this.semanticModel.GetSymbolInfo(syntax);

            if (symbol is ResourceSymbol {
                IsCollection : true
            } || symbol is ModuleSymbol {
예제 #9
0
        private DeclaredTypeAssignment?GetVariableAccessType(VariableAccessSyntax syntax)
        {
            // because all variable access nodes are normally bound to something, this should always return true
            // (if not, the following code handles that gracefully)
            var symbol = this.binder.GetSymbolInfo(syntax);

            switch (symbol)
            {
            case ResourceSymbol resourceSymbol when IsCycleFree(resourceSymbol):
                // the declared type of the resource/loop/if body is more useful to us than the declared type of the resource itself
                var innerResourceBody = resourceSymbol.DeclaringResource.Value;

                return(this.GetDeclaredTypeAssignment(innerResourceBody));

            case ModuleSymbol moduleSymbol when IsCycleFree(moduleSymbol):
                // the declared type of the module/loop/if body is more useful to us than the declared type of the module itself
                var innerModuleBody = moduleSymbol.DeclaringModule.Value;

                return(this.GetDeclaredTypeAssignment(innerModuleBody));

            case DeclaredSymbol declaredSymbol when IsCycleFree(declaredSymbol):
                // the syntax node is referencing a declared symbol
                // use its declared type
                return(this.GetDeclaredTypeAssignment(declaredSymbol.DeclaringSyntax));

            case NamespaceSymbol namespaceSymbol:
                // the syntax node is referencing a namespace - use its type
                return(new DeclaredTypeAssignment(namespaceSymbol.Type, declaringSyntax: null));
            }

            return(null);
        }
예제 #10
0
        public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            this.ValidateDirectAccessToResourceOrModuleCollection(syntax);

            // visit children
            base.VisitVariableAccessSyntax(syntax);
        }
예제 #11
0
        private void VisitVariableAccessSyntaxInternal(VariableAccessSyntax syntax)
        {
            if (currentDeclaration == null)
            {
                return;
            }

            switch (model.GetSymbolInfo(syntax))
            {
            case VariableSymbol variableSymbol:
                if (!resourceDependencies.TryGetValue(variableSymbol, out var dependencies))
                {
                    // recursively visit dependent variables
                    this.Visit(variableSymbol.DeclaringSyntax);

                    dependencies = resourceDependencies[variableSymbol];
                }

                foreach (var dependency in dependencies)
                {
                    resourceDependencies[currentDeclaration].Add(dependency);
                }
                return;

            case ResourceSymbol resourceSymbol:
                resourceDependencies[currentDeclaration].Add(resourceSymbol);
                return;

            case ModuleSymbol moduleSymbol:
                resourceDependencies[currentDeclaration].Add(moduleSymbol);
                return;
            }
        }
예제 #12
0
        private TypeSymbol GetParameterType(TypeManagerContext context, VariableAccessSyntax syntax, ParameterSymbol parameter)
        {
            // parameter default values can participate in cycles with their own parameters or other symbols
            // need to explicitly force a type check to detect that
            SyntaxBase?expressionToTypeCheck;

            switch (parameter.Modifier)
            {
            case ParameterDefaultValueSyntax defaultValueSyntax:
                expressionToTypeCheck = defaultValueSyntax.DefaultValue;
                break;

            case ObjectSyntax modifierSyntax:
                // technically it's redundant to type check the entire object because we have existing
                // compile-time constant checks, but it shouldn't harm anything
                expressionToTypeCheck = modifierSyntax;
                break;

            case null:
                // there's no default value or modifier - this parameter cannot participate in a cycle
                expressionToTypeCheck = null;
                break;

            default:
                throw new NotImplementedException($"Unexpected parameter modifier type '{parameter.Modifier.GetType()}");
            }

            if (expressionToTypeCheck != null && ContainsCyclicExpressionError(this.GetTypeInfoInternal(context, expressionToTypeCheck)))
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Name.Span).CyclicExpression()));
            }

            return(HandleSymbolType(syntax.Name.IdentifierName, syntax.Name.Span, parameter.Type));
        }
예제 #13
0
        public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            base.VisitVariableAccessSyntax(syntax);

            var symbol = this.LookupSymbolByName(syntax.Name, false);

            // bind what we got - the type checker will validate if it fits
            this.bindings.Add(syntax, symbol);
        }
예제 #14
0
        public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            if (currentDeclaration == null)
            {
                throw new ArgumentException($"Variable access outside of declaration");
            }

            declarationAccessDict[currentDeclaration].Add(syntax);
            base.VisitVariableAccessSyntax(syntax);
        }
예제 #15
0
        private void VisitVariableAccessSyntaxInternal(VariableAccessSyntax syntax)
        {
            if (currentDeclaration == null)
            {
                return;
            }

            if (shouldInlineCache[currentDeclaration] == Decision.Inline)
            {
                // we've already made a decision to inline
                return;
            }

            switch (model.GetSymbolInfo(syntax))
            {
            case VariableSymbol variableSymbol:
                var previousStack = this.currentStack;
                if (!shouldInlineCache.TryGetValue(variableSymbol, out var shouldInline))
                {
                    this.currentStack = this.currentStack?.Push(syntax.Name.IdentifierName);

                    // recursively visit dependent variables
                    this.Visit(variableSymbol.DeclaringSyntax);

                    shouldInline = shouldInlineCache[variableSymbol];

                    if (shouldInline == Decision.Inline && this.targetVariable is not null && this.capturedSequence is null)
                    {
                        // this point is where the decision is made to inline the variable
                        // the variable access stack will be the deepest here
                        // (once captured, we will not reset because the visitor will be short-circuiting and
                        //  unrolling the recursion which would produce shorter and inaccurate paths)

                        // capture the sequence of variable accesses
                        this.capturedSequence = this.currentStack;
                    }
                }

                // if we depend on a variable that requires inlining, then we also require inlining
                var newValue = shouldInlineCache[currentDeclaration] == Decision.Inline || shouldInline == Decision.Inline;
                SetInlineCache(newValue);

                this.currentStack = previousStack;
                return;

            case ResourceSymbol:
            case ModuleSymbol:
                if (this.currentDeclaration is not null && shouldInlineCache[currentDeclaration] != Decision.SkipInline)
                {
                    //inline only if declaration wasn't explicitly excluded from inlining, to avoid inlining usages which are permitted
                    SetInlineCache(true);
                }
                return;
            }
        }
예제 #16
0
        private TypeSymbol GetResourceType(TypeManagerContext context, VariableAccessSyntax syntax, ResourceSymbol resource)
        {
            // resource bodies can participate in cycles
            // need to explicitly force a type check on the body
            if (ContainsCyclicExpressionError(this.GetTypeInfoInternal(context, resource.Body)))
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Name.Span).CyclicExpression()));
            }

            return(HandleSymbolType(syntax.Name.IdentifierName, syntax.Name.Span, resource.Type));
        }
예제 #17
0
        public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            if (!currentDeclarations.TryPeek(out var currentDeclaration))
            {
                // we're not inside a declaration, so there should be no risk of a cycle
                return;
            }

            declarationAccessDict[currentDeclaration].Add(syntax);
            base.VisitVariableAccessSyntax(syntax);
        }
예제 #18
0
        protected override SyntaxBase ReplaceVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            if (this.semanticModel.GetSymbolInfo(syntax) is not {
            } symbol || !this.replacements.TryGetValue(symbol, out var replacementSyntax))
            {
                // unbound variable access or not a symbol that we need to replace
                // leave syntax as-is
                return(base.ReplaceVariableAccessSyntax(syntax));
            }

            // inject the replacement syntax
            return(replacementSyntax);
        }
예제 #19
0
        public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            if (!currentDeclarations.TryPeek(out var currentDeclaration))
            {
                if (currentDecorator != null)
                {
                    // We are inside a dangling decorator.
                    return;
                }

                throw new ArgumentException($"Variable access outside of declaration");
            }

            declarationAccessDict[currentDeclaration].Add(syntax);
            base.VisitVariableAccessSyntax(syntax);
        }
예제 #20
0
        private void VisitPropertyAccessSyntaxInternal(PropertyAccessSyntax syntax)
        {
            // This solution works on the assumption that all deploy-time constants are top-level properties on
            // resources and modules (id, name, type, apiVersion). Once https://github.com/Azure/bicep/issues/1177 is fixed,
            // it should be possible to make this more robust to handle nested deploy-time constants.

            if (currentDeclaration == null)
            {
                return;
            }

            var variableAccessSyntax = syntax.BaseExpression switch
            {
                VariableAccessSyntax variableAccess => variableAccess,
                ArrayAccessSyntax {
                    BaseExpression : VariableAccessSyntax variableAccess
                } => variableAccess,
예제 #21
0
            public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
            {
                // Look for references of secure parameters, e.g.:
                //
                //   @secure()
                //   param secureParam string
                //   output badResult string = 'this is the value ${secureParam}'

                Symbol?symbol = model.GetSymbolInfo(syntax);

                if (symbol is ParameterSymbol param)
                {
                    if (param.IsSecure())
                    {
                        string foundMessage = string.Format(CoreResources.OutputsShouldNotContainSecretsSecureParam, syntax.Name.IdentifierName);
                        this.diagnostics.Add(parent.CreateDiagnosticForSpan(syntax.Name.Span, foundMessage));
                    }
                }

                base.VisitVariableAccessSyntax(syntax);
            }
        public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            var symbol = semanticModel.GetSymbolInfo(syntax);

            switch (symbol)
            {
            case ResourceSymbol resourceSymbol:
                resourceDependencies.Add(resourceSymbol);
                return;

            case ModuleSymbol moduleSymbol:
                resourceDependencies.Add(moduleSymbol);
                return;

            case VariableSymbol variableSymbol:
                Visit(variableSymbol.DeclaringSyntax);
                return;

            default:
                return;
            }
        }
예제 #23
0
        public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
        {
            var baseSymbol = model.GetSymbolInfo(syntax);

            switch (baseSymbol)
            {
            case VariableSymbol variableSymbol:
                // 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.invalidReferencedBodyObj != null)
                {
                    this.errorSyntax          = syntax;
                    this.referencedBodyObj    = variableVisitor.invalidReferencedBodyObj;
                    this.variableVisitorStack = variableVisitor.visitedStack;
                    this.accessedSymbol       = variableVisitor.visitedStack.Peek();
                }
                break;
            }
        }
예제 #24
0
 private TypeSymbol GetResourceType(VariableAccessSyntax syntax, ResourceSymbol resource)
 {
     // resource bodies can participate in cycles
     // need to explicitly force a type check on the body
     return(HandleSymbolType(syntax.Name.IdentifierName, syntax.Name.Span, resource));
 }
예제 #25
0
 public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
 {
     VisitVariableAccessSyntaxInternal(syntax);
     base.VisitVariableAccessSyntax(syntax);
 }
예제 #26
0
 public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
 {
     this.AppendError(syntax);
 }
예제 #27
0
        protected override SyntaxBase ReplaceForSyntax(ForSyntax syntax)
        {
            syntax = (ForSyntax)base.ReplaceForSyntax(syntax);

            // look for range(0, length(<variable>))
            if (!IsLoopIteratorExpression(semanticModel, syntax.Expression, out var arrayVariable))
            {
                return(syntax);
            }

            if (syntax.VariableSection is not LocalVariableSyntax indexVariable)
            {
                return(syntax);
            }

            var arraySymbol      = semanticModel.GetSymbolInfo(arrayVariable);
            var arrayIndexSymbol = semanticModel.GetSymbolInfo(indexVariable);

            if (arraySymbol is null || arrayIndexSymbol is null)
            {
                return(syntax);
            }

            var arrayAccesses            = new HashSet <ArrayAccessSyntax>();
            var independentIndexAccesses = new HashSet <VariableAccessSyntax>();

            CallbackVisitor.Visit(syntax, child =>
            {
                if (child is ArrayAccessSyntax arrayAccess)
                {
                    if (semanticModel.GetSymbolInfo(arrayAccess.BaseExpression) == arraySymbol &&
                        semanticModel.GetSymbolInfo(arrayAccess.IndexExpression) == arrayIndexSymbol)
                    {
                        arrayAccesses.Add(arrayAccess);

                        // we don't want to count the VariableAccessSyntax under this particular node,
                        // so return false to skip visiting children.
                        return(false);
                    }
                }

                if (child is VariableAccessSyntax variableAccess)
                {
                    var accessSymbol = semanticModel.GetSymbolInfo(variableAccess);
                    if (accessSymbol == arrayIndexSymbol)
                    {
                        independentIndexAccesses.Add(variableAccess);
                    }
                }

                return(true);
            });

            if (!arrayAccesses.Any())
            {
                // nothing to really simplify here
                return(syntax);
            }

            var itemVarName = GetUniqueVariableNameForNewScope(syntax, "item");
            var forBody     = CallbackRewriter.Rewrite(syntax.Body, child =>
            {
                if (arrayAccesses.Contains(child))
                {
                    return(new VariableAccessSyntax(SyntaxFactory.CreateIdentifier(itemVarName)));
                }

                return(child);
            });

            SyntaxBase forVariableBlockSyntax;

            if (independentIndexAccesses.Any())
            {
                forVariableBlockSyntax = new ForVariableBlockSyntax(
                    SyntaxFactory.LeftParenToken,
                    new LocalVariableSyntax(SyntaxFactory.CreateIdentifier(itemVarName)),
                    SyntaxFactory.CommaToken,
                    new LocalVariableSyntax(SyntaxFactory.CreateIdentifier(arrayIndexSymbol.Name)),
                    SyntaxFactory.RightParenToken);
            }
            else
            {
                forVariableBlockSyntax = new LocalVariableSyntax(SyntaxFactory.CreateIdentifier(itemVarName));
            }

            var forExpression = new VariableAccessSyntax(SyntaxFactory.CreateIdentifier(arraySymbol.Name));

            return(new ForSyntax(
                       syntax.OpenSquare,
                       syntax.ForKeyword,
                       forVariableBlockSyntax,
                       syntax.InKeyword,
                       forExpression,
                       syntax.Colon,
                       forBody,
                       syntax.CloseSquare));
        }
예제 #28
0
        public static (DeclaredSymbol?, ObjectType?) ExtractResourceOrModuleSymbolAndBodyObj(SemanticModel model, VariableAccessSyntax syntax)
        {
            var baseSymbol = model.GetSymbolInfo(syntax);

            switch (baseSymbol)
            {
            case ResourceSymbol:
            case ModuleSymbol:
                var declaredSymbol = (DeclaredSymbol)baseSymbol;
                var unwrapped      = TypeAssignmentVisitor.UnwrapType(declaredSymbol.Type);
                return(declaredSymbol, unwrapped is ObjectType ? (ObjectType)unwrapped : null);
            }
            return(null, null);
        }