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; } } }
protected override SyntaxBase ReplacePropertyAccessSyntax(PropertyAccessSyntax syntax) { var baseType = semanticModel.GetDeclaredType(syntax.BaseExpression); if (baseType is not ObjectType objectType) { return(base.ReplacePropertyAccessSyntax(syntax)); } var propertyName = syntax.PropertyName.IdentifierName; if (objectType.Properties.ContainsKey(propertyName)) { return(base.ReplacePropertyAccessSyntax(syntax)); } var insensitivePropertyName = objectType.Properties.Keys.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Equals(x, propertyName)); if (insensitivePropertyName is null) { return(base.ReplacePropertyAccessSyntax(syntax)); } var propertySyntax = new IdentifierSyntax(new Token(TokenType.Identifier, new TextSpan(0, 0), insensitivePropertyName, Enumerable.Empty <SyntaxTrivia>(), Enumerable.Empty <SyntaxTrivia>())); return(new PropertyAccessSyntax( syntax.BaseExpression, syntax.Dot, propertySyntax)); }
private TypeSymbol GetPropertyAccessType(TypeManagerContext context, PropertyAccessSyntax syntax) { var errors = new List <ErrorDiagnostic>(); var baseType = this.GetTypeInfoInternal(context, syntax.BaseExpression); CollectErrors(errors, baseType); if (errors.Any()) { return(new ErrorTypeSymbol(errors)); } if (TypeValidator.AreTypesAssignable(baseType, LanguageConstants.Object) != true) { // can only access properties of objects return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.PropertyName).ObjectRequiredForPropertyAccess(baseType))); } if (baseType.TypeKind == TypeKind.Any || !(baseType is ObjectType objectType)) { return(LanguageConstants.Any); } return(this.GetNamedPropertyType(objectType, syntax.PropertyName, syntax.PropertyName.IdentifierName)); }
private static InvocationExpressionSyntax CreateCombineSyntax(IEnumerable <PropertyDeclarationSyntax> properties) { var args = properties.Select(p => Argument(PropertyAccessSyntax.Create(p.Identifier))); // HashCode.Combine(this.Property1, this.Property2) return(InvocationExpression(AccessMemberSyntax.Create("HashCode.Combine")) .WithArgumentList(ArgumentList(SeparatedList(args)))); }
public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax) { if (IsCallToRgOrDeploymentLocation(syntax, out string?actualExpression)) { this.ActualExpression = actualExpression; return; } base.VisitPropertyAccessSyntax(syntax); }
public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax) { if (IsCallToRgOrDeploymentLocation(syntax, out string?actualExpression)) { string msg = String.Format( CoreResources.NoLocExprOutsideParamsRuleError, actualExpression); this.diagnostics.Add(parent.CreateDiagnosticForSpan(syntax.Span, msg)); } base.VisitPropertyAccessSyntax(syntax); }
public async Task GoToDefinitionOnUnboundSyntaxNodeShouldReturnEmptyResponse(DataSet dataSet) { // local function bool IsUnboundNode(IDictionary <SyntaxBase, Symbol> dictionary, SyntaxBase syntax) => dictionary.ContainsKey(syntax) == false && !(syntax is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var unboundNodes = SyntaxAggregator.Aggregate( source: compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, seed: new List <SyntaxBase>(), function: (accumulated, syntax) => { if (IsUnboundNode(symbolTable, syntax) && !(syntax is ProgramSyntax)) { // only collect unbound nodes non-program nodes accumulated.Add(syntax); } return(accumulated); }, resultSelector: accumulated => accumulated, // visit children only if current node is not bound continuationFunction: (accumulated, syntax) => IsUnboundNode(symbolTable, syntax)); foreach (var syntax in unboundNodes) { var offset = syntax switch { // base expression could be a variable access which is bound and will throw off the test PropertyAccessSyntax propertyAccess => propertyAccess.PropertyName.Span.Position, ArrayAccessSyntax arrayAccess => arrayAccess.OpenSquare.Span.Position, _ => syntax.Span.Position }; var response = await client.RequestDefinition(new DefinitionParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, offset) }); // go to definition on a syntax node that isn't bound to a symbol should produce an empty response response.Should().BeEmpty(); } }
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; } if (shouldInlineCache[currentDeclaration] == Decision.Inline) { // we've already made a decision to inline return; }
private DeclaredTypeAssignment?GetPropertyAccessType(PropertyAccessSyntax syntax) { if (!syntax.PropertyName.IsValid) { return(null); } var baseExpressionAssignment = GetDeclaredTypeAssignment(syntax.BaseExpression); // it's ok to rely on useSyntax=true because those types have already been established return(GetObjectPropertyType( baseExpressionAssignment?.Reference.Type, baseExpressionAssignment?.DeclaringSyntax as ObjectSyntax, syntax.PropertyName.IdentifierName, useSyntax: true)); }
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; } } }
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,
private void VisitPropertyAccessSyntaxInternal(PropertyAccessSyntax syntax) { if (currentDeclaration == null) { return; } if (!(syntax.BaseExpression is VariableAccessSyntax variableAccessSyntax)) { // we only care about variable access return; } if (shouldInlineCache[currentDeclaration]) { // we've already made a decision return; } switch (model.GetSymbolInfo(variableAccessSyntax)) { case ResourceSymbol resourceSymbol: if (!(resourceSymbol.Type is ResourceType resourceType && resourceType.Body is ObjectType bodyObjectType)) { // Resource or body could be an ErrorType here. We only want to attempt property access on an object body. return; } var property = syntax.PropertyName.IdentifierName; if (!bodyObjectType.Properties.TryGetValue(property, out var propertyType)) { // unknown property return; } // update the cache if property can't be skipped for inlining shouldInlineCache[currentDeclaration] |= !propertyType.Flags.HasFlag(TypePropertyFlags.SkipInlining); return; } }
public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax) => this.BuildWithConcat(() => base.VisitPropertyAccessSyntax(syntax));
private (ModuleSymbol moduleSymbol, string outputName)? TryGetModulePropertyAccess(PropertyAccessSyntax propertyAccess) { // is this a (<child>.outputs).<prop> propertyAccess? if (!(propertyAccess.BaseExpression is PropertyAccessSyntax childPropertyAccess) || childPropertyAccess.PropertyName.IdentifierName != LanguageConstants.ModuleOutputsPropertyName) { return(null); } // is <child> a variable which points to a module symbol? if (!(childPropertyAccess.BaseExpression is VariableAccessSyntax grandChildVariableAccess) || !(context.SemanticModel.GetSymbolInfo(grandChildVariableAccess) is ModuleSymbol moduleSymbol)) { return(null); } return(moduleSymbol, propertyAccess.PropertyName.IdentifierName); }
private LanguageExpression ConvertPropertyAccess(PropertyAccessSyntax propertyAccess) { if (context.SemanticModel.ResourceMetadata.TryLookup(propertyAccess.BaseExpression) is { } resource) { var movedSyntax = context.Settings.EnableSymbolicNames ? resource.Symbol.NameSyntax : resource.NameSyntax; // we are doing property access on a single resource return(CreateConverterForIndexReplacement(movedSyntax, null, propertyAccess) .ConvertResourcePropertyAccess(resource, null, propertyAccess.PropertyName.IdentifierName)); } if (propertyAccess.BaseExpression is ArrayAccessSyntax propArrayAccess && context.SemanticModel.ResourceMetadata.TryLookup(propArrayAccess.BaseExpression) is { } resourceCollection) { var movedSyntax = context.Settings.EnableSymbolicNames ? resourceCollection.Symbol.NameSyntax : resourceCollection.NameSyntax; // we are doing property access on an array access of a resource collection return(CreateConverterForIndexReplacement(movedSyntax, propArrayAccess.IndexExpression, propertyAccess) .ConvertResourcePropertyAccess(resourceCollection, propArrayAccess.IndexExpression, propertyAccess.PropertyName.IdentifierName)); } if (propertyAccess.BaseExpression is VariableAccessSyntax modulePropVariableAccess && context.SemanticModel.GetSymbolInfo(modulePropVariableAccess) is ModuleSymbol moduleSymbol && CreateConverterForIndexReplacement(GetModuleNameSyntax(moduleSymbol), null, propertyAccess) .ConvertModulePropertyAccess(moduleSymbol, propertyAccess.PropertyName.IdentifierName) is { } moduleConvertedSingle) { // we are doing property access on a single module // and we are dealing with special case properties return(moduleConvertedSingle); } if (propertyAccess.BaseExpression is ArrayAccessSyntax modulePropArrayAccess && modulePropArrayAccess.BaseExpression is VariableAccessSyntax moduleArrayVariableAccess && context.SemanticModel.GetSymbolInfo(moduleArrayVariableAccess) is ModuleSymbol moduleCollectionSymbol && CreateConverterForIndexReplacement(GetModuleNameSyntax(moduleCollectionSymbol), modulePropArrayAccess.IndexExpression, propertyAccess) .ConvertModulePropertyAccess(moduleCollectionSymbol, propertyAccess.PropertyName.IdentifierName) is { } moduleConvertedCollection) { // we are doing property access on an array access of a module collection // and we are dealing with special case properties return(moduleConvertedCollection); } // is this a (<child>.outputs).<prop> propertyAccess? if (propertyAccess.BaseExpression is PropertyAccessSyntax childPropertyAccess && childPropertyAccess.PropertyName.IdentifierName == LanguageConstants.ModuleOutputsPropertyName) { switch (childPropertyAccess.BaseExpression) { // is <child> a variable which points to a variable that requires in-lining? case VariableAccessSyntax grandChildVariableAccess when context.SemanticModel.GetSymbolInfo(grandChildVariableAccess) is VariableSymbol variableSymbol && context.VariablesToInline.Contains(variableSymbol): { //execute variable in-lining if (ConvertVariableAccess(grandChildVariableAccess) is FunctionExpression moduleReferenceExpression) { // we assume that this will generate a proper reference function to a deployment resource. // If not then the deployment will fail as the template will be malformed but that should have been caught before return(AppendProperties(moduleReferenceExpression, new JTokenExpression(propertyAccess.PropertyName.IdentifierName), new JTokenExpression("value"))); } break; } // is <child> a variable which points to a non-collection module symbol? case VariableAccessSyntax grandChildVariableAccess when context.SemanticModel.GetSymbolInfo(grandChildVariableAccess) is ModuleSymbol { IsCollection: false } outputsModuleSymbol: { return(AppendProperties( this.GetModuleOutputsReferenceExpression(outputsModuleSymbol, null), new JTokenExpression(propertyAccess.PropertyName.IdentifierName), new JTokenExpression("value"))); } // is <child> an array access operating on a module collection case ArrayAccessSyntax { BaseExpression: VariableAccessSyntax grandGrandChildVariableAccess } grandChildArrayAccess when context.SemanticModel.GetSymbolInfo(grandGrandChildVariableAccess) is ModuleSymbol { IsCollection : true } outputsModuleCollectionSymbol : { var updatedConverter = this.CreateConverterForIndexReplacement(GetModuleNameSyntax(outputsModuleCollectionSymbol), grandChildArrayAccess.IndexExpression, propertyAccess); return(AppendProperties( updatedConverter.GetModuleOutputsReferenceExpression(outputsModuleCollectionSymbol, grandChildArrayAccess.IndexExpression), new JTokenExpression(propertyAccess.PropertyName.IdentifierName), new JTokenExpression("value"))); } } } return(AppendProperties( ToFunctionExpression(propertyAccess.BaseExpression), new JTokenExpression(propertyAccess.PropertyName.IdentifierName))); }
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); }
public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax) { this.buffer.Append('('); base.VisitPropertyAccessSyntax(syntax); this.buffer.Append(')'); }
public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax) { this.AppendError(syntax); }
public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax) { VisitPropertyAccessSyntaxInternal(syntax); base.VisitPropertyAccessSyntax(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; } if (!(syntax.BaseExpression is VariableAccessSyntax variableAccessSyntax)) { // we only care about variable access return; } if (shouldInlineCache[currentDeclaration]) { // we've already made a decision return; } bool shouldSkipInlining(ObjectType objectType, string propertyName) { var property = syntax.PropertyName.IdentifierName; if (!objectType.Properties.TryGetValue(propertyName, out var propertyType)) { // unknown property - we should assume it's not inlinable return(true); } // update the cache if property can't be skipped for inlining return(propertyType.Flags.HasFlag(TypePropertyFlags.DeployTimeConstant)); } switch (model.GetSymbolInfo(variableAccessSyntax)) { // Note - there's a limitation here that we're using the 'declared' type and not the 'assigned' type. // This means that we may encounter a DiscriminatedObjectType. For now we should accept this limitation, // and move to using the assigned type once https://github.com/Azure/bicep/issues/1177 is fixed. case ResourceSymbol resourceSymbol: { if (!(resourceSymbol.Type is ResourceType resourceType && resourceType.Body.Type is ObjectType bodyObjectType)) { // Resource or body could be an ErrorType here. We only want to attempt property access on an object body. return; } shouldInlineCache[currentDeclaration] = !shouldSkipInlining(bodyObjectType, syntax.PropertyName.IdentifierName); return; } case ModuleSymbol moduleSymbol: { if (!(moduleSymbol.Type is ModuleType moduleType && moduleType.Body.Type is ObjectType bodyObjectType)) { // Resource or body could be an ErrorType here. We only want to attempt property access on an object body. return; } shouldInlineCache[currentDeclaration] = !shouldSkipInlining(bodyObjectType, syntax.PropertyName.IdentifierName); return; } } }