Example #1
0
        public override void VisitResourceDeclarationSyntax(ResourceDeclarationSyntax syntax)
        => AssignTypeWithCaching(syntax, () => {
            var stringSyntax = syntax.TryGetType();

            if (stringSyntax != null && stringSyntax.IsInterpolated())
            {
                // TODO: in the future, we can relax this check to allow interpolation with compile-time constants.
                // right now, codegen will still generate a format string however, which will cause problems for the type.
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).ResourceTypeInterpolationUnsupported()));
            }

            var stringContent = stringSyntax?.GetLiteralValue();
            if (stringContent == null)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).InvalidResourceType()));
            }

            // TODO: This needs proper namespace, type, and version resolution logic in the future
            var typeReference = ResourceTypeReference.TryParse(stringContent);
            if (typeReference == null)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).InvalidResourceType()));
            }

            // TODO: Construct/lookup type information based on JSON schema or swagger
            // for now assuming very basic resource schema
            return(new ResourceType(stringContent, LanguageConstants.CreateResourceProperties(typeReference), additionalProperties: null, typeReference));
        });
Example #2
0
        private IEnumerable <Diagnostic> ValidateIdentifierAccess(ParameterDeclarationSyntax syntax)
        {
            return(SyntaxAggregator.Aggregate(syntax, new List <Diagnostic>(), (accumulated, current) =>
            {
                if (current is VariableAccessSyntax)
                {
                    var symbol = bindings[current];

                    // Error: already has error info attached, no need to add more
                    // Parameter: references are permitted in other parameters' default values as long as there is not a cycle (BCP080)
                    // Function: we already validate that a function cannot be used as a variable (BCP063)
                    // Output: we already validate that outputs cannot be referenced in expressions (BCP058)
                    if (symbol.Kind != SymbolKind.Error &&
                        symbol.Kind != SymbolKind.Parameter &&
                        symbol.Kind != SymbolKind.Function &&
                        symbol.Kind != SymbolKind.Output)
                    {
                        accumulated.Add(DiagnosticBuilder.ForPosition(current).CannotReferenceSymbolInParamDefaultValue());
                    }
                }

                return accumulated;
            },
                                              accumulated => accumulated));
        }
Example #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));
        }
Example #4
0
        private DeclaredTypeAssignment?GetResourceAccessType(ResourceAccessSyntax syntax)
        {
            if (!syntax.ResourceName.IsValid)
            {
                return(null);
            }

            // We should already have a symbol, use its type.
            var symbol = this.binder.GetSymbolInfo(syntax);

            if (symbol == null)
            {
                throw new InvalidOperationException("ResourceAccessSyntax was not assigned a symbol during name binding.");
            }

            if (symbol is ErrorSymbol error)
            {
                return(new DeclaredTypeAssignment(ErrorType.Create(error.GetDiagnostics()), syntax));
            }
            else if (symbol is not ResourceSymbol resourceSymbol)
            {
                var baseType   = GetDeclaredType(syntax.BaseExpression);
                var typeString = baseType?.Kind.ToString() ?? LanguageConstants.ErrorName;
                return(new DeclaredTypeAssignment(ErrorType.Create(DiagnosticBuilder.ForPosition(syntax.ResourceName).ResourceRequiredForResourceAccess(typeString)), syntax));
            }
Example #5
0
        public override void VisitResourceDeclarationSyntax(ResourceDeclarationSyntax syntax)
        => AssignTypeWithDiagnostics(syntax, diagnostics => {
            var stringSyntax = syntax.TryGetType();

            if (stringSyntax != null && stringSyntax.IsInterpolated())
            {
                // TODO: in the future, we can relax this check to allow interpolation with compile-time constants.
                // right now, codegen will still generate a format string however, which will cause problems for the type.
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).ResourceTypeInterpolationUnsupported()));
            }

            var stringContent = stringSyntax?.TryGetLiteralValue();
            if (stringContent == null)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).InvalidResourceType()));
            }

            // TODO: This needs proper namespace, type, and version resolution logic in the future
            var typeReference = ResourceTypeReference.TryParse(stringContent);
            if (typeReference == null)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).InvalidResourceType()));
            }

            var declaredType = resourceTypeRegistrar.GetType(typeReference);

            return(TypeValidator.NarrowTypeAndCollectDiagnostics(typeManager, syntax.Body, declaredType, diagnostics));
        });
Example #6
0
        private TypeSymbol GetUnaryOperationType(TypeManagerContext context, UnaryOperationSyntax syntax)
        {
            var errors = new List <ErrorDiagnostic>();

            // TODO: When we add number type, this will have to be adjusted
            var expectedOperandType = syntax.Operator switch
            {
                UnaryOperator.Not => LanguageConstants.Bool,
                UnaryOperator.Minus => LanguageConstants.Int,
                _ => throw new NotImplementedException()
            };

            var operandType = this.GetTypeInfoInternal(context, syntax.Expression);

            CollectErrors(errors, operandType);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            if (TypeValidator.AreTypesAssignable(operandType, expectedOperandType) != true)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax).UnaryOperatorInvalidType(Operators.UnaryOperatorToText[syntax.Operator], operandType.Name)));
            }

            return(expectedOperandType);
        }
Example #7
0
        private TypeSymbol GetTernaryOperationType(TypeManagerContext context, TernaryOperationSyntax syntax)
        {
            var errors = new List <ErrorDiagnostic>();

            // ternary operator requires the condition to be of bool type
            var conditionType = this.GetTypeInfoInternal(context, syntax.ConditionExpression);

            CollectErrors(errors, conditionType);

            var trueType = this.GetTypeInfoInternal(context, syntax.TrueExpression);

            CollectErrors(errors, trueType);

            var falseType = this.GetTypeInfoInternal(context, syntax.FalseExpression);

            CollectErrors(errors, falseType);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            var expectedConditionType = LanguageConstants.Bool;

            if (TypeValidator.AreTypesAssignable(conditionType, expectedConditionType) != true)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.ConditionExpression).ValueTypeMismatch(expectedConditionType.Name)));
            }

            // the return type is the union of true and false expression types
            return(UnionType.Create(trueType, falseType));
        }
Example #8
0
        public override void VisitBinaryOperationSyntax(BinaryOperationSyntax syntax)
        => AssignTypeWithCaching(syntax, () => {
            var errors = new List <ErrorDiagnostic>();

            var operandType1 = VisitAndReturnType(syntax.LeftExpression);
            CollectErrors(errors, operandType1);

            var operandType2 = VisitAndReturnType(syntax.RightExpression);
            CollectErrors(errors, operandType2);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // operands don't appear to have errors
            // let's match the operator now
            var operatorInfo = BinaryOperationResolver.TryMatchExact(syntax.Operator, operandType1, operandType2);
            if (operatorInfo != null)
            {
                // we found a match - use its return type
                return(operatorInfo.ReturnType);
            }

            // we do not have a match
            // operand types didn't match available operators
            return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax).BinaryOperatorInvalidType(Operators.BinaryOperatorToText[syntax.Operator], operandType1.Name, operandType2.Name)));
        });
Example #9
0
        protected void FlagDeployTimeConstantViolation(SyntaxBase errorSyntax, DeclaredSymbol?accessedSymbol = null, ObjectType?accessedObjectType = null, IEnumerable <string>?variableDependencyChain = null)
        {
            var accessedSymbolName      = accessedSymbol?.Name;
            var accessiblePropertyNames = GetAccessiblePropertyNames(accessedSymbol, accessedObjectType);

            var containerObjectSyntax = this.DeployTimeConstantContainer is ObjectPropertySyntax
                ? this.SemanticModel.Binder.GetParent(this.DeployTimeConstantContainer) as ObjectSyntax
                : null;

            var containerObjectType = containerObjectSyntax is not null
                ? this.SemanticModel.TypeManager.GetDeclaredType(containerObjectSyntax)
                : null;

            var diagnosticBuilder = DiagnosticBuilder.ForPosition(errorSyntax);
            var diagnostic        = this.DeployTimeConstantContainer switch
            {
                ObjectPropertySyntax propertySyntax when propertySyntax.TryGetKeyText() is
                {
                }

                propertyName =>
                diagnosticBuilder.RuntimeValueNotAllowedInProperty(propertyName, containerObjectType?.Name, accessedSymbolName, accessiblePropertyNames, variableDependencyChain),
                IfConditionSyntax => diagnosticBuilder.RuntimeValueNotAllowedInIfConditionExpression(accessedSymbolName, accessiblePropertyNames, variableDependencyChain),

                // Corner case: the runtime value is in the for-body of a variable declaration.
                ForSyntax forSyntax when ErrorSyntaxInForBodyOfVariable(forSyntax, errorSyntax) is string variableName =>
                diagnosticBuilder.RuntimeValueNotAllowedInVariableForBody(variableName, accessedSymbolName, accessiblePropertyNames, variableDependencyChain),

                ForSyntax => diagnosticBuilder.RuntimeValueNotAllowedInForExpression(accessedSymbolName, accessiblePropertyNames, variableDependencyChain),
                FunctionCallSyntaxBase functionCallSyntaxBase => diagnosticBuilder.RuntimeValueNotAllowedInRunTimeFunctionArguments(functionCallSyntaxBase.Name.IdentifierName, accessedSymbolName, accessiblePropertyNames, variableDependencyChain),
                _ => throw new ArgumentOutOfRangeException(nameof(this.DeployTimeConstantContainer), "Expected an ObjectPropertySyntax with a propertyName, a IfConditionSyntax, a ForSyntax, or a FunctionCallSyntaxBase."),
            };

            this.DiagnosticWriter.Write(diagnostic);
        }
        public Task <BicepRegistryCacheResponse> Handle(BicepRegistryCacheParams request, CancellationToken cancellationToken)
        {
            // If any of the following paths result in an exception being thrown (and surfaced client-side to the user),
            // it indicates a code defect client or server-side.
            // In normal operation, the user should never see them regardless of how malformed their code is.

            // TODO: Add documentUri to BicepRegistryCacheParams and get the config for the documentUri.
            var configuration   = this.configurationManager.GetBuiltInConfiguration();
            var moduleReference = this.moduleDispatcher.TryGetModuleReference(request.Target, configuration, out _) ?? throw new InvalidOperationException($"The client specified an invalid module reference '{request.Target}'.");

            if (!moduleReference.IsExternal)
            {
                throw new InvalidOperationException($"The specified module reference '{request.Target}' refers to a local module which is not supported by {BicepCacheLspMethod} requests.");
            }

            if (this.moduleDispatcher.GetModuleRestoreStatus(moduleReference, out _) != ModuleRestoreStatus.Succeeded)
            {
                throw new InvalidOperationException($"The module '{moduleReference.FullyQualifiedReference}' has not yet been successfully restored.");
            }

            var uri = this.moduleDispatcher.TryGetLocalModuleEntryPointUri(null, moduleReference, out _) ?? throw new InvalidOperationException($"Unable to obtain the entry point URI for module '{moduleReference.FullyQualifiedReference}'.");

            if (!this.fileResolver.TryRead(uri, out var contents, out var failureBuilder))
            {
                var message = failureBuilder(DiagnosticBuilder.ForPosition(new TextSpan(0, 0))).Message;
                throw new InvalidOperationException($"Unable to read file '{uri}'. {message}");
            }

            return(Task.FromResult(new BicepRegistryCacheResponse(contents)));
        }
Example #11
0
        public override void VisitTernaryOperationSyntax(TernaryOperationSyntax syntax)
        => AssignTypeWithCaching(syntax, () => {
            var errors = new List <ErrorDiagnostic>();

            // ternary operator requires the condition to be of bool type
            var conditionType = VisitAndReturnType(syntax.ConditionExpression);
            CollectErrors(errors, conditionType);

            var trueType = VisitAndReturnType(syntax.TrueExpression);
            CollectErrors(errors, trueType);

            var falseType = VisitAndReturnType(syntax.FalseExpression);
            CollectErrors(errors, falseType);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            var expectedConditionType = LanguageConstants.Bool;
            if (TypeValidator.AreTypesAssignable(conditionType, expectedConditionType) != true)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.ConditionExpression).ValueTypeMismatch(expectedConditionType.Name)));
            }

            // the return type is the union of true and false expression types
            return(UnionType.Create(trueType, falseType));
        });
        private TypeSymbol?CheckForCyclicError(SyntaxBase syntax)
        {
            if (this.binder.GetSymbolInfo(syntax) is not DeclaredSymbol declaredSymbol)
            {
                return(null);
            }

            if (declaredSymbol.DeclaringSyntax == syntax)
            {
                // Report cycle errors on accesses to cyclic symbols, not on the declaration itself
                return(null);
            }

            if (this.binder.TryGetCycle(declaredSymbol) is { } cycle)
            {
                // there's a cycle. stop visiting now or we never will!
                if (cycle.Length == 1)
                {
                    return(ErrorType.Create(DiagnosticBuilder.ForPosition(syntax).CyclicExpressionSelfReference()));
                }

                return(ErrorType.Create(DiagnosticBuilder.ForPosition(syntax).CyclicExpression(cycle.Select(x => x.Name))));
            }

            return(null);
        }
Example #13
0
        public TypeSymbol GetAssignedType(ITypeManager typeManager, ArraySyntax?allowedSyntax)
        {
            var assignedType = this.GetDeclaredType();

            // TODO: remove SyntaxHelper.TryGetAllowedSyntax when we drop parameter modifiers support.
            if (allowedSyntax is not null && !allowedSyntax.Items.Any())
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(allowedSyntax).AllowedMustContainItems()));
            }

            var allowedItemTypes = allowedSyntax?.Items.Select(typeManager.GetTypeInfo);

            if (ReferenceEquals(assignedType, LanguageConstants.String))
            {
                if (allowedItemTypes?.All(itemType => itemType is StringLiteralType) == true)
                {
                    assignedType = UnionType.Create(allowedItemTypes);
                }
                else
                {
                    // In order to support assignment for a generic string to enum-typed properties (which generally is forbidden),
                    // we need to relax the validation for string parameters without 'allowed' values specified.
                    assignedType = LanguageConstants.LooseString;
                }
            }

            if (ReferenceEquals(assignedType, LanguageConstants.Array) &&
                allowedItemTypes?.All(itemType => itemType is StringLiteralType) == true)
            {
                assignedType = new TypedArrayType(UnionType.Create(allowedItemTypes), TypeSymbolValidationFlags.Default);
            }

            return(assignedType);
        }
Example #14
0
        private Symbol LookupGlobalSymbolByName(IdentifierSyntax identifierSyntax, bool isFunctionCall)
        {
            // attempt to find name in the built in namespaces. imported namespaces will be present in the declarations list as they create declared symbols.
            if (this.namespaceResolver.BuiltIns.TryGetValue(identifierSyntax.IdentifierName) is { } namespaceSymbol)
            {
                // namespace symbol found
                return(namespaceSymbol);
            }

            // declarations must not have a namespace value, namespaces are used to fully qualify a function access.
            // There might be instances where a variable declaration for example uses the same name as one of the imported
            // functions, in this case to differentiate a variable declaration vs a function access we check the namespace value,
            // the former case must have an empty namespace value whereas the latter will have a namespace value.
            if (this.declarations.TryGetValue(identifierSyntax.IdentifierName, out var globalSymbol))
            {
                // we found the symbol in the global namespace
                return(globalSymbol);
            }

            // attempt to find function in all imported namespaces
            var foundSymbols = namespaceResolver.ResolveUnqualifiedFunction(identifierSyntax, includeDecorators: allowedFlags.HasAnyDecoratorFlag());

            if (foundSymbols.Count() > 1)
            {
                // ambiguous symbol
                return(new ErrorSymbol(DiagnosticBuilder.ForPosition(identifierSyntax).AmbiguousSymbolReference(identifierSyntax.IdentifierName, namespaceResolver.GetNamespaceNames().ToImmutableSortedSet(StringComparer.Ordinal))));
            }

            var foundSymbol = foundSymbols.FirstOrDefault();

            return(isFunctionCall ?
                   SymbolValidator.ResolveUnqualifiedFunction(allowedFlags, foundSymbol, identifierSyntax, namespaceResolver) :
                   SymbolValidator.ResolveUnqualifiedSymbol(foundSymbol, identifierSyntax, namespaceResolver, declarations.Keys));
        }
Example #15
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)));
            }
        }
Example #16
0
        private Symbol LookupSymbolByName(string name, TextSpan span)
        {
            if (this.declarations.TryGetValue(name, out var localSymbol))
            {
                // we found the symbol in the local namespace
                return(ValidateFunctionFlags(localSymbol, span));
            }

            // symbol does not exist in the local namespace
            // try it in the imported namespaces

            // match in one of the namespaces
            var foundSymbols = this.namespaces
                               .Select(ns => ns.TryGetFunctionSymbol(name))
                               .Where(symbol => symbol != null)
                               .ToList();

            if (foundSymbols.Count() > 1)
            {
                // ambiguous symbol
                return(new ErrorSymbol(DiagnosticBuilder.ForPosition(span).AmbiguousSymbolReference(name, this.namespaces.Select(ns => ns.Name))));
            }

            var foundSymbol = foundSymbols.FirstOrDefault();

            if (foundSymbol == null)
            {
                return(new ErrorSymbol(DiagnosticBuilder.ForPosition(span).SymbolicNameDoesNotExist(name)));
            }

            return(ValidateFunctionFlags(foundSymbol, span));
        }
Example #17
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));
        }
Example #18
0
        private static IEnumerable <Diagnostic> DetectDuplicateNames(SemanticModel semanticModel, ImmutableDictionary <ResourceSymbol, ScopeHelper.ScopeData> resourceScopeData, ImmutableDictionary <ModuleSymbol, ScopeHelper.ScopeData> moduleScopeData)
        {
            // This method only checks, if in one deployment we do not have 2 or more resources with this same name in one deployment to avoid template validation error
            // This will not check resource constraints such as necessity of having unique virtual network names within resource group

            var duplicateResources = GetResourceDefinitions(semanticModel, resourceScopeData)
                                     .GroupBy(x => x, ResourceDefinition.EqualityComparer)
                                     .Where(group => group.Count() > 1);

            foreach (var duplicatedResourceGroup in duplicateResources)
            {
                var duplicatedResourceNames = duplicatedResourceGroup.Select(x => x.ResourceName).ToArray();
                foreach (var duplicatedResource in duplicatedResourceGroup)
                {
                    yield return(DiagnosticBuilder.ForPosition(duplicatedResource.ResourceNamePropertyValue).ResourceMultipleDeclarations(duplicatedResourceNames));
                }
            }

            var duplicateModules = GetModuleDefinitions(semanticModel, moduleScopeData)
                                   .GroupBy(x => x, ModuleDefinition.EqualityComparer)
                                   .Where(group => group.Count() > 1);

            foreach (var duplicatedModuleGroup in duplicateModules)
            {
                var duplicatedModuleNames = duplicatedModuleGroup.Select(x => x.ModuleName).ToArray();
                foreach (var duplicatedModule in duplicatedModuleGroup)
                {
                    yield return(DiagnosticBuilder.ForPosition(duplicatedModule.ModulePropertyNameValue).ModuleMultipleDeclarations(duplicatedModuleNames));
                }
            }
        }
Example #19
0
        private TypeSymbol GetBinaryOperationType(TypeManagerContext context, BinaryOperationSyntax syntax)
        {
            var errors = new List <ErrorDiagnostic>();

            var operandType1 = this.GetTypeInfoInternal(context, syntax.LeftExpression);

            CollectErrors(errors, operandType1);

            var operandType2 = this.GetTypeInfoInternal(context, syntax.RightExpression);

            CollectErrors(errors, operandType2);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // operands don't appear to have errors
            // let's match the operator now
            var operatorInfo = BinaryOperationResolver.TryMatchExact(syntax.Operator, operandType1, operandType2);

            if (operatorInfo != null)
            {
                // we found a match - use its return type
                return(operatorInfo.ReturnType);
            }

            // we do not have a match
            // operand types didn't match available operators
            return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax).BinaryOperatorInvalidType(Operators.BinaryOperatorToText[syntax.Operator], operandType1.Name, operandType2.Name)));
        }
Example #20
0
        /// <summary>
        /// Gets the type of the property whose name we can obtain at compile-time.
        /// </summary>
        /// <param name="baseType">The base object type</param>
        /// <param name="propertyExpressionPositionable">The position of the property name expression</param>
        /// <param name="propertyName">The resolved property name</param>
        private TypeSymbol GetNamedPropertyType(ObjectType baseType, IPositionable propertyExpressionPositionable, string propertyName)
        {
            if (baseType.TypeKind == TypeKind.Any)
            {
                // all properties of "any" type are of type "any"
                return(LanguageConstants.Any);
            }

            // is there a declared property with this name
            var declaredProperty = baseType.Properties.TryGetValue(propertyName);

            if (declaredProperty != null)
            {
                if (declaredProperty.Flags.HasFlag(TypePropertyFlags.WriteOnly))
                {
                    return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(propertyExpressionPositionable).WriteOnlyProperty(baseType, propertyName)));
                }

                // there is - return its type
                return(declaredProperty.Type);
            }

            // the property is not declared
            // check additional properties
            if (baseType.AdditionalPropertiesType != null)
            {
                // yes - return the additional property type
                return(baseType.AdditionalPropertiesType);
            }

            return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(propertyExpressionPositionable).UnknownProperty(baseType, propertyName)));
        }
Example #21
0
        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;
        }
Example #22
0
        public override void VisitForSyntax(ForSyntax syntax)
        {
            // save previous property loop count on the call stack
            var previousPropertyLoopCount = this.propertyLoopCount;

            switch (this.IsLoopAllowedHere(syntax))
            {
            case false:
                // this loop was used incorrectly
                this.diagnosticWriter.Write(DiagnosticBuilder.ForPosition(syntax.ForKeyword).ForExpressionsNotSupportedHere());
                break;

            case null:
                // this is a property loop
                this.propertyLoopCount += 1;

                if (this.propertyLoopCount > MaximumNestedPropertyLoopCount)
                {
                    // too many property loops
                    this.diagnosticWriter.Write(DiagnosticBuilder.ForPosition(syntax.ForKeyword).TooManyPropertyForExpressions());
                }

                break;
            }

            // visit children
            base.VisitForSyntax(syntax);

            // restore previous property loop count
            this.propertyLoopCount = previousPropertyLoopCount;
        }
Example #23
0
        public void DiagnosticBuilder_CodesAreUnique()
        {
            var diagnosticMethods = typeof(DiagnosticBuilder.DiagnosticBuilderInternal)
                                    .GetMethods(BindingFlags.Instance | BindingFlags.Public)
                                    .Where(m => typeof(Diagnostic).IsAssignableFrom(m.ReturnType));

            // verify the above Linq is actually working
            diagnosticMethods.Should().HaveCountGreaterThan(40);

            var builder = DiagnosticBuilder.ForPosition(new TextSpan(0, 10));

            var definedCodes = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (var diagnosticMethod in diagnosticMethods)
            {
                var mockParams = diagnosticMethod.GetParameters().Select(CreateMockParameter);

                var diagnostic = diagnosticMethod.Invoke(builder, mockParams.ToArray()) as Diagnostic;

                if (mockParams.Any())
                {
                    // verify that all the params are actually being written in the message
                    diagnostic !.Message.Should().ContainAll(CollectExpectedStrings(mockParams), $"method {diagnosticMethod.Name} should use all of its parameters in the format string.");
                }

                // verify that the Code is unique
                definedCodes.Should().NotContain(diagnostic !.Code, $"Method {diagnosticMethod.Name} should be assigned a unique error code.");
                definedCodes.Add(diagnostic !.Code);
            }
        }
Example #24
0
            private void ValidateScope(ILanguageScope scope)
            {
                // collect duplicate identifiers at this scope
                // declaring a variable in a local scope hides the parent scope variables,
                // so we don't need to look at other levels
                var outputDeclarations    = scope.Declarations.Where(decl => decl is OutputSymbol);
                var nonOutputDeclarations = scope.Declarations.Where(decl => decl is not OutputSymbol);

                // all symbols apart from outputs are in the same namespace, so check for uniqueness.
                this.Diagnostics.AddRange(
                    FindDuplicateNamedSymbols(nonOutputDeclarations)
                    .Select(decl => DiagnosticBuilder.ForPosition(decl.NameSyntax).IdentifierMultipleDeclarations(decl.Name)));

                // output symbols cannot be referenced, so the names declared by them do not need to be unique in the scope.
                // we still need to ensure that they unique among other outputs.
                this.Diagnostics.AddRange(
                    FindDuplicateNamedSymbols(outputDeclarations)
                    .Select(decl => DiagnosticBuilder.ForPosition(decl.NameSyntax).OutputMultipleDeclarations(decl.Name)));

                // imported namespaces are reserved in all the scopes
                // otherwise the user could accidentally hide a namespace which would remove the ability
                // to fully qualify a function
                this.Diagnostics.AddRange(nonOutputDeclarations
                                          .Where(decl => decl.NameSyntax.IsValid && this.importedNamespaces.ContainsKey(decl.Name))
                                          .Select(reservedSymbol => DiagnosticBuilder.ForPosition(reservedSymbol.NameSyntax).SymbolicNameCannotUseReservedNamespaceName(reservedSymbol.Name, this.importedNamespaces.Keys)));
            }
        public TypeSymbol GetDeclaredType(ResourceScope targetScope, IResourceTypeProvider resourceTypeProvider)
        {
            var stringSyntax = this.TypeString;

            if (stringSyntax != null && stringSyntax.IsInterpolated())
            {
                // TODO: in the future, we can relax this check to allow interpolation with compile-time constants.
                // right now, codegen will still generate a format string however, which will cause problems for the type.
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).ResourceTypeInterpolationUnsupported()));
            }

            var stringContent = stringSyntax?.TryGetLiteralValue();

            if (stringContent == null)
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceType()));
            }

            var typeReference = ResourceTypeReference.TryParse(stringContent);

            if (typeReference == null)
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(this.Type).InvalidResourceType()));
            }

            return(resourceTypeProvider.GetType(targetScope, typeReference));
        }
        public TypeSymbol GetAssignedType(ITypeManager typeManager)
        {
            var assignedType = this.GetDeclaredType();

            var allowedSyntax = SyntaxHelper.TryGetAllowedSyntax(this);

            if (allowedSyntax != null && !allowedSyntax.Items.Any())
            {
                return(ErrorType.Create(DiagnosticBuilder.ForPosition(allowedSyntax).AllowedMustContainItems()));
            }

            if (object.ReferenceEquals(assignedType, LanguageConstants.String))
            {
                var allowedItemTypes = allowedSyntax?.Items.Select(typeManager.GetTypeInfo);

                if (allowedItemTypes != null && allowedItemTypes.All(itemType => itemType is StringLiteralType))
                {
                    assignedType = UnionType.Create(allowedItemTypes);
                }
                else
                {
                    // In order to support assignment for a generic string to enum-typed properties (which generally is forbidden),
                    // we need to relax the validation for string parameters without 'allowed' values specified.
                    assignedType = LanguageConstants.LooseString;
                }
            }

            return(assignedType);
        }
        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;
        }
Example #28
0
        private TypeSymbol?CheckForCyclicError(SyntaxBase syntax)
        {
            if (!(bindings.TryGetValue(syntax, out var symbol) && (symbol is DeclaredSymbol declaredSymbol)))
            {
                return(null);
            }

            if (declaredSymbol.DeclaringSyntax == syntax)
            {
                // Report cycle errors on accesses to cyclic symbols, not on the declaration itself
                return(null);
            }

            if (cyclesBySymbol.TryGetValue(declaredSymbol, out var cycle))
            {
                // there's a cycle. stop visiting now or we never will!
                if (cycle.Length == 1)
                {
                    return(ErrorType.Create(DiagnosticBuilder.ForPosition(syntax).CyclicExpressionSelfReference()));
                }

                return(ErrorType.Create(DiagnosticBuilder.ForPosition(syntax).CyclicExpression(cycle.Select(x => x.Name))));
            }

            return(null);
        }
Example #29
0
 static DecoratorValidator ValidateTargetType(TypeSymbol attachableType) =>
 (decoratorName, decoratorSyntax, targetType, _, diagnosticWriter) =>
 {
     if (!TypeValidator.AreTypesAssignable(targetType, attachableType))
     {
         diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).CannotAttacheDecoratorToTarget(decoratorName, attachableType, targetType));
     }
 };
        public override void VisitIdentifierSyntax(IdentifierSyntax syntax)
        {
            if (syntax.IdentifierName.Length > LanguageConstants.MaxIdentifierLength)
            {
                this.diagnostics.Add(DiagnosticBuilder.ForPosition(syntax.Identifier).IdentifierNameExceedsLimit());
            }

            base.VisitIdentifierSyntax(syntax);
        }