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(); } } }
public void EmitOptionalPropertyExpression(string name, SyntaxBase?expression) { if (expression != null) { EmitPropertyExpression(name, expression); } }
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; }
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; } } }
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; }
private void VisitCommaAndNewLineSeparated(ImmutableArray <SyntaxBase> nodes, bool leadingAndTrailingSpace) { SyntaxBase?leadingNewLine = null; if (nodes.Length > 0 && nodes[0] is Token { Type : TokenType.NewLine })
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; } } }
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)); }
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 }, });
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; }
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; }
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); }
public void Visit(SyntaxBase?node) { if (node == null) { return; } VisitInternal(node); }
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); }
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); }
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(); }
public void Visit(SyntaxBase?node) { if (node == null) { return; } RuntimeHelpers.EnsureSufficientExecutionStack(); VisitInternal(node); }
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); }
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; }
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; }
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(", ")}"); } }
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); }
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; } } }
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); }