Exemplo n.º 1
0
 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;
         }
     }
 }
Exemplo n.º 2
0
        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));
        }
Exemplo n.º 3
0
        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));
        }
Exemplo n.º 4
0
        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))));
        }
Exemplo n.º 5
0
            public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax)
            {
                if (IsCallToRgOrDeploymentLocation(syntax, out string?actualExpression))
                {
                    this.ActualExpression = actualExpression;
                    return;
                }

                base.VisitPropertyAccessSyntax(syntax);
            }
Exemplo n.º 6
0
            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);
            }
Exemplo n.º 7
0
        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();
            }
        }
Exemplo n.º 8
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;
            }

            if (shouldInlineCache[currentDeclaration] == Decision.Inline)
            {
                // we've already made a decision to inline
                return;
            }
Exemplo n.º 9
0
        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;
                    }
                }
            }
Exemplo n.º 11
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,
Exemplo n.º 12
0
        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;
            }
        }
Exemplo n.º 13
0
 public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax) =>
 this.BuildWithConcat(() => base.VisitPropertyAccessSyntax(syntax));
Exemplo n.º 14
0
        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);
        }
Exemplo n.º 15
0
        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)));
        }
Exemplo n.º 16
0
        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);
                }
Exemplo n.º 17
0
 public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax)
 {
     this.buffer.Append('(');
     base.VisitPropertyAccessSyntax(syntax);
     this.buffer.Append(')');
 }
Exemplo n.º 18
0
 public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax)
 {
     this.AppendError(syntax);
 }
Exemplo n.º 19
0
 public override void VisitPropertyAccessSyntax(PropertyAccessSyntax syntax)
 {
     VisitPropertyAccessSyntaxInternal(syntax);
     base.VisitPropertyAccessSyntax(syntax);
 }
Exemplo n.º 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;
            }

            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;
            }
            }
        }