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); }
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))); } }
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; } }
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); }
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 {
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); }
public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax) { this.ValidateDirectAccessToResourceOrModuleCollection(syntax); // visit children base.VisitVariableAccessSyntax(syntax); }
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; } }
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)); }
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); }
public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax) { if (currentDeclaration == null) { throw new ArgumentException($"Variable access outside of declaration"); } declarationAccessDict[currentDeclaration].Add(syntax); base.VisitVariableAccessSyntax(syntax); }
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; } }
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)); }
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); }
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); }
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); }
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,
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; } }
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; } }
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)); }
public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax) { VisitVariableAccessSyntaxInternal(syntax); base.VisitVariableAccessSyntax(syntax); }
public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax) { this.AppendError(syntax); }
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)); }
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); }