コード例 #1
0
        public void SymbolicNameDoesNotExistWithSuggestion_ProducesPreferredNameReplacementCodeFix()
        {
            var builder = DiagnosticBuilder.ForPosition(new TextSpan(0, 10));

            var diagnostic = builder.SymbolicNameDoesNotExistWithSuggestion("hellO", "hello");

            diagnostic.Fixes.Should().NotBeNull();
            diagnostic.Fixes.Should().HaveCount(1);

            var fix = diagnostic.Fixes.First();

            fix.IsPreferred.Should().BeTrue();
            fix.Replacements.Should().NotBeNull();
            fix.Replacements.Should().HaveCount(1);

            var replacement = fix.Replacements.First();

            replacement.Span.Should().Be(diagnostic.Span);
            replacement.Text.Should().Be("hello");
        }
コード例 #2
0
        public void UnknownPropertyWithSuggestion_ProducesPreferredPropertyReplacementCodeFix()
        {
            var builder = DiagnosticBuilder.ForPosition(new TextSpan(0, 10));

            var diagnostic = builder.UnknownPropertyWithSuggestion(false, new PrimitiveType("testType", TypeSymbolValidationFlags.Default), "networkACLs", "networkAcls");

            diagnostic.Fixes.Should().NotBeNull();
            diagnostic.Fixes.Should().HaveCount(1);

            var fix = diagnostic.Fixes.First();

            fix.IsPreferred.Should().BeTrue();
            fix.Replacements.Should().NotBeNull();
            fix.Replacements.Should().HaveCount(1);

            var replacement = fix.Replacements.First();

            replacement.Span.Should().Be(diagnostic.Span);
            replacement.Text.Should().Be("networkAcls");
        }
コード例 #3
0
ファイル: Decorator.cs プロジェクト: tommagumma/bicep
        public void Validate(DecoratorSyntax decoratorSyntax, TypeSymbol targetType, ITypeManager typeManager, IDiagnosticWriter diagnosticWriter)
        {
            if (targetType is ErrorType)
            {
                return;
            }

            if (this.validator != null)
            {
                this.validator.Invoke(this.Overload.Name, decoratorSyntax, targetType, typeManager, diagnosticWriter);

                return;
            }

            // No custom validator provided. Just validate the target type.
            if (!this.CanAttachTo(targetType))
            {
                diagnosticWriter.Write(DiagnosticBuilder.ForPosition(decoratorSyntax).CannotAttacheDecoratorToTarget(this.Overload.Name, attachableType, targetType));
            }
        }
コード例 #4
0
        protected override void VisitInternal(SyntaxBase node)
        {
            var kind     = GetExpressionKind(node);
            var newState = GetNewState(kind);

            if (newState == VisitorState.SecondOperatorChainInsideObjectLiteralInsideFirstOperatorChain)
            {
                this.diagnostics.Add(DiagnosticBuilder.ForPosition(node.Span).ExpressionContainsObjectLiteralContainingOtherExpressions());

                // we won't suddenly regain the ability to compile the expression by visiting children,
                // so let's not continue deeper into the tree
                return;
            }

            this.stack.Push(newState);

            base.VisitInternal(node);

            this.stack.Pop();
        }
コード例 #5
0
        public void ShouldReturnSubstitutionsInString()
        {
            Diagnostics diags;
            string      schemaName
                = $"{ResourceLocationPrefix}.Docs.DiagnosticSchema.xml";

            using (Stream s
                       = typeof(DependencyInjector).Assembly.GetManifestResourceStream(schemaName))
            {
                DiagnosticBuilder db = new DiagnosticBuilder(s);
                diags = db.Diagnostics;
                dynamic diag = diags.Groups["InvalidBean"].CreateDiagnostic();
                diag.AbstractOrStaticClass = "testBaseClassType";
                diag.ClassMode             = "MyClassMode";
                diags.Groups["InvalidBean"].Add(diag);
            }
            string str = diags.ToString();

            Assert.IsTrue(str.Contains("testBaseClassType"));
        }
コード例 #6
0
ファイル: NameBindingVisitor.cs プロジェクト: iayongwa/bicep
        public override void VisitInstanceFunctionCallSyntax(InstanceFunctionCallSyntax syntax)
        {
            base.VisitInstanceFunctionCallSyntax(syntax);

            Symbol foundSymbol;

            // baseExpression must be bound to a namespaceSymbol otherwise there was an error
            if (bindings.ContainsKey(syntax.BaseExpression) &&
                bindings[syntax.BaseExpression] is NamespaceSymbol namespaceSymbol)
            {
                foundSymbol = this.LookupSymbolByName(syntax.Name.IdentifierName, syntax.Name.Span, namespaceSymbol);
            }
            else
            {
                foundSymbol = new UnassignableSymbol(DiagnosticBuilder.ForPosition(syntax.Name.Span).SymbolicNameDoesNotExist(syntax.Name.IdentifierName));
            }

            // bind what we got - the type checker will validate if it fits
            this.bindings.Add(syntax, foundSymbol);
        }
コード例 #7
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)
            {
                var nameCandidates = this.declarations.Values
                                     .Concat(this.namespaces.SelectMany(ns => ns.Descendants))
                                     .Select(symbol => symbol.Name)
                                     .ToImmutableSortedSet();

                var suggestedName = SpellChecker.GetSpellingSuggestion(name, nameCandidates);

                return(suggestedName != null
                    ? new ErrorSymbol(DiagnosticBuilder.ForPosition(span).SymbolicNameDoesNotExistWithSuggestion(name, suggestedName))
                    : new ErrorSymbol(DiagnosticBuilder.ForPosition(span).SymbolicNameDoesNotExist(name)));
            }

            return(ValidateFunctionFlags(foundSymbol, span));
        }
コード例 #8
0
        public override void VisitParameterDeclarationSyntax(ParameterDeclarationSyntax syntax)
        => AssignTypeWithDiagnostics(syntax, diagnostics => {
            diagnostics.AddRange(this.ValidateIdentifierAccess(syntax));

            // assume "any" type when the parameter has parse errors (either missing or was skipped)
            var declaredType = syntax.ParameterType == null
                    ? LanguageConstants.Any
                    : LanguageConstants.TryGetDeclarationType(syntax.ParameterType.TypeName);

            if (declaredType == null)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).InvalidParameterType()));
            }

            var assignedType = declaredType;
            if (object.ReferenceEquals(assignedType, LanguageConstants.String))
            {
                var allowedItemTypes = SyntaxHelper.TryGetAllowedItems(syntax)?
                                       .Select(item => typeManager.GetTypeInfo(item));

                if (allowedItemTypes != null && allowedItemTypes.All(itemType => itemType is StringLiteralType))
                {
                    assignedType = UnionType.Create(allowedItemTypes);
                }
            }

            switch (syntax.Modifier)
            {
            case ParameterDefaultValueSyntax defaultValueSyntax:
                diagnostics.AddRange(ValidateDefaultValue(defaultValueSyntax, assignedType));
                break;

            case ObjectSyntax modifierSyntax:
                var modifierType = LanguageConstants.CreateParameterModifierType(declaredType, assignedType);
                // we don't need to actually use the narrowed type; just need to use this to collect assignment diagnostics
                TypeValidator.NarrowTypeAndCollectDiagnostics(typeManager, modifierSyntax, modifierType, diagnostics);
                break;
            }

            return(assignedType);
        });
コード例 #9
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 true when this.activeLoopCapableTopLevelDeclaration is VariableDeclarationSyntax variable && InlineDependencyVisitor.ShouldInlineVariable(this.semanticModel, variable, out var variableChain):
                // this is a loop variable that has a dependency on functions that are not supported in JSON variables
                // we are initially blocking this because not all cases can be generated in the JSON

                // unable to get a detailed variable dependency chain
                // log a generic error instead and put it on the "for" keyword
                this.diagnosticWriter.Write(DiagnosticBuilder.ForPosition(syntax.ForKeyword).VariableLoopsRuntimeDependencyNotAllowed(variableChain));

                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;
        }
コード例 #10
0
        private Symbol ValidateFunctionFlags(Symbol symbol, TextSpan span)
        {
            if (!(symbol is FunctionSymbol functionSymbol))
            {
                return(symbol);
            }

            var functionFlags = functionSymbol.Overloads.Select(overload => overload.Flags).Aggregate((x, y) => x | y);

            if (functionFlags.HasFlag(FunctionFlags.ParamDefaultsOnly) && !allowedFlags.HasFlag(FunctionFlags.ParamDefaultsOnly))
            {
                return(new ErrorSymbol(DiagnosticBuilder.ForPosition(span).FunctionOnlyValidInParameterDefaults(functionSymbol.Name)));
            }

            if (functionFlags.HasFlag(FunctionFlags.RequiresInlining) && !allowedFlags.HasFlag(FunctionFlags.RequiresInlining))
            {
                return(new ErrorSymbol(DiagnosticBuilder.ForPosition(span).FunctionOnlyValidInResourceBody(functionSymbol.Name)));
            }

            return(symbol);
        }
コード例 #11
0
        private static void GetArrayAssignmentDiagnostics(ITypeManager typeManager, ArraySyntax expression, ArrayType targetType, IList <Diagnostic> diagnostics, bool skipConstantCheck)
        {
            // if we have parse errors, no need to check assignability
            // we should not return the parse errors however because they will get double collected
            if (expression.HasParseErrors())
            {
                return;
            }

            foreach (var arrayItemSyntax in expression.Items)
            {
                GetExpressionAssignmentDiagnosticsInternal(
                    typeManager,
                    arrayItemSyntax.Value,
                    targetType.Item.Type,
                    diagnostics,
                    (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).ArrayTypeMismatch(expectedType.Name, actualType.Name),
                    skipConstantCheck,
                    skipTypeErrors: true);
            }
        }
コード例 #12
0
        public TypeSymbol GetAssignedType(ITypeManager typeManager, ArraySyntax?allowedSyntax)
        {
            var assignedType = typeManager.GetDeclaredType(this);

            if (assignedType is null)
            {
                // We don't expect this to happen for a parameter.
                return(ErrorType.Empty());
            }

            // 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 = TypeHelper.CreateTypeUnion(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(TypeHelper.CreateTypeUnion(allowedItemTypes), TypeSymbolValidationFlags.Default);
            }

            return(assignedType);
        }
コード例 #13
0
        private SyntaxTreeGrouping Build(Uri entryFileUri)
        {
            var entryPoint = PopulateRecursive(entryFileUri, out var entryPointLoadFailureBuilder);

            if (entryPoint == null)
            {
                // TODO: If we upgrade to netstandard2.1, we should be able to use the following to hint to the compiler that failureBuilder is non-null:
                // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis
                var failureBuilder = entryPointLoadFailureBuilder ?? throw new InvalidOperationException($"Expected {nameof(PopulateRecursive)} to provide failure diagnostics");
                var diagnostic     = failureBuilder(DiagnosticBuilder.ForPosition(new TextSpan(0, 0)));

                throw new ErrorDiagnosticException(diagnostic);
            }

            ReportFailuresForCycles();

            return(new SyntaxTreeGrouping(
                       entryPoint,
                       syntaxTrees.Values.OfType <SyntaxTree>().ToImmutableHashSet(),
                       moduleLookup.ToImmutableDictionary(),
                       moduleFailureLookup.ToImmutableDictionary()));
        }
コード例 #14
0
        private TypeSymbol?CheckForCyclicError(SyntaxBase syntax)
        {
            if (cyclesBySyntax.TryGetValue(syntax, out var cycle))
            {
                // there's a cycle. stop visiting now or we never will!
                if (cycle.Length == 1)
                {
                    return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax).CyclicSelfReference()));
                }

                var syntaxBinding = bindings[syntax];

                // show the cycle as originating from the current syntax symbol
                var cycleSuffix  = cycle.TakeWhile(x => x != syntaxBinding);
                var cyclePrefix  = cycle.Skip(cycleSuffix.Count());
                var orderedCycle = cyclePrefix.Concat(cycleSuffix);

                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax).CyclicExpression(orderedCycle.Select(x => x.Name))));
            }

            return(null);
        }
コード例 #15
0
        private Symbol LookupSymbolByName(IdentifierSyntax identifierSyntax, bool isFunctionCall)
        {
            // attempt to find name in the imported namespaces
            if (this.namespaces.TryGetValue(identifierSyntax.IdentifierName, out var 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 localSymbol))
            {
                // we found the symbol in the local namespace
                return(localSymbol);
            }

            // attempt to find function in all imported namespaces
            var foundSymbols = this.namespaces
                               .Select(kvp => allowedFlags.HasDecoratorFlag()
                    ? kvp.Value.Type.MethodResolver.TryGetSymbol(identifierSyntax) ?? kvp.Value.Type.DecoratorResolver.TryGetSymbol(identifierSyntax)
                    : kvp.Value.Type.MethodResolver.TryGetSymbol(identifierSyntax))
                               .Where(symbol => symbol != null)
                               .ToList();

            if (foundSymbols.Count > 1)
            {
                // ambiguous symbol
                return(new ErrorSymbol(DiagnosticBuilder.ForPosition(identifierSyntax).AmbiguousSymbolReference(identifierSyntax.IdentifierName, this.namespaces.Keys)));
            }

            var foundSymbol = foundSymbols.FirstOrDefault();

            return(isFunctionCall ?
                   SymbolValidator.ResolveUnqualifiedFunction(allowedFlags, foundSymbol, identifierSyntax, namespaces.Values) :
                   SymbolValidator.ResolveUnqualifiedSymbol(foundSymbol, identifierSyntax, namespaces.Values, declarations.Keys));
        }
コード例 #16
0
        private static TypeSymbol LoadTextContentTypeBuilder(IBinder binder, IFileResolver fileResolver, IDiagnosticWriter diagnostics, ImmutableArray <FunctionArgumentSyntax> arguments, ImmutableArray <TypeSymbol> argumentTypes)
        {
            if (argumentTypes[0] is not StringLiteralType filePathType)
            {
                diagnostics.Write(DiagnosticBuilder.ForPosition(arguments[0]).CompileTimeConstantRequired());
                return(LanguageConstants.String);
            }
            var filePathValue = filePathType.RawStringValue;

            var fileUri = GetFileUriWithDiagnostics(binder, fileResolver, diagnostics, filePathValue, arguments[0]);

            if (fileUri is null)
            {
                return(LanguageConstants.String);
            }
            var fileEncoding = Encoding.UTF8;

            if (argumentTypes.Length > 1)
            {
                if (argumentTypes[1] is not StringLiteralType encodingType)
                {
                    diagnostics.Write(DiagnosticBuilder.ForPosition(arguments[1]).CompileTimeConstantRequired());
                    return(LanguageConstants.String);
                }
                fileEncoding = LanguageConstants.SupportedEncodings.First(x => string.Equals(x.name, encodingType.RawStringValue, LanguageConstants.IdentifierComparison)).encoding;
            }

            if (!fileResolver.TryRead(fileUri, out var fileContent, out var fileReadFailureBuilder, fileEncoding, LanguageConstants.MaxLiteralCharacterLimit, out var detectedEncoding))
            {
                diagnostics.Write(fileReadFailureBuilder.Invoke(DiagnosticBuilder.ForPosition(arguments[0])));
                return(LanguageConstants.String);
            }
            if (arguments.Length > 1 && fileEncoding != detectedEncoding)
            {
                diagnostics.Write(DiagnosticBuilder.ForPosition(arguments[1]).FileEncodingMismatch(detectedEncoding.WebName));
            }
            return(new StringLiteralType(fileContent));
        }
コード例 #17
0
        private static TypeSymbol LoadContentAsBase64TypeBuilder(IBinder binder, IFileResolver fileResolver, IDiagnosticWriter diagnostics, ImmutableArray <FunctionArgumentSyntax> arguments, ImmutableArray <TypeSymbol> argumentTypes)
        {
            if (argumentTypes[0] is not StringLiteralType filePathType)
            {
                diagnostics.Write(DiagnosticBuilder.ForPosition(arguments[0]).CompileTimeConstantRequired());
                return(LanguageConstants.String);
            }
            var filePathValue = filePathType.RawStringValue;

            var fileUri = GetFileUriWithDiagnostics(binder, fileResolver, diagnostics, filePathValue, arguments[0]);

            if (fileUri is null)
            {
                return(LanguageConstants.String);
            }
            if (!fileResolver.TryReadAsBase64(fileUri, out var fileContent, out var fileReadFailureBuilder, LanguageConstants.MaxLiteralCharacterLimit))
            {
                diagnostics.Write(fileReadFailureBuilder.Invoke(DiagnosticBuilder.ForPosition(arguments[0])));
                return(LanguageConstants.String);
            }

            return(new StringLiteralType(binder.FileSymbol.FileUri.MakeRelativeUri(fileUri).ToString(), fileContent));
        }
コード例 #18
0
ファイル: TypeManager.cs プロジェクト: ryanazuretest/bicep
        private TypeSymbol GetFunctionCallType(TypeManagerContext context, FunctionCallSyntax syntax)
        {
            var errors        = new List <ErrorDiagnostic>();
            var argumentTypes = syntax.Arguments.Select(syntax1 => GetTypeInfoInternal(context, syntax1)).ToList();

            foreach (TypeSymbol argumentType in argumentTypes)
            {
                CollectErrors(errors, argumentType);
            }

            switch (this.ResolveSymbol(syntax))
            {
            case ErrorSymbol errorSymbol:
                // function bind failure - pass the error along
                return(new ErrorTypeSymbol(errors.Concat(errorSymbol.GetDiagnostics())));

            case FunctionSymbol function:
                return(GetFunctionSymbolType(syntax, function, syntax.Arguments, argumentTypes, errors));

            default:
                return(new ErrorTypeSymbol(errors.Append(DiagnosticBuilder.ForPosition(syntax.Name.Span).SymbolicNameIsNotAFunction(syntax.Name.IdentifierName))));
            }
        }
コード例 #19
0
        public override void VisitParameterDeclarationSyntax(ParameterDeclarationSyntax syntax)
        => AssignTypeWithCaching(syntax, () => {
            var primitiveType = LanguageConstants.TryGetDeclarationType(syntax.Type.TypeName);
            if (primitiveType == null)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Type).InvalidParameterType()));
            }

            if (!object.ReferenceEquals(primitiveType, LanguageConstants.String))
            {
                return(primitiveType);
            }

            var allowedItemTypes = SyntaxHelper.TryGetAllowedItems(syntax)?
                                   .Select(item => VisitAndReturnType(item));

            if (allowedItemTypes == null || !allowedItemTypes.All(itemType => itemType is StringLiteralType))
            {
                return(primitiveType);
            }

            return(UnionType.Create(allowedItemTypes));
        });
コード例 #20
0
        public override void VisitObjectPropertySyntax(ObjectPropertySyntax syntax)
        {
            if (syntax.TryGetKeyText() == null && syntax.Value is ForSyntax)
            {
                // block loop usage with properties whose names are expressions
                this.diagnosticWriter.Write(DiagnosticBuilder.ForPosition(syntax.Key).ExpressionedPropertiesNotAllowedWithLoops());
            }

            bool insideDependsOnInThisScope = ReferenceEquals(this.currentDependsOnProperty, syntax);

            // set this to true if the current property is the top-level dependsOn property
            // leave it true if already set to true
            this.insideTopLevelDependsOn = this.insideTopLevelDependsOn || insideDependsOnInThisScope;

            // visit children
            base.VisitObjectPropertySyntax(syntax);

            // clear the flag after we leave the dependsOn property
            if (insideDependsOnInThisScope)
            {
                this.insideTopLevelDependsOn = false;
            }
        }
コード例 #21
0
        private static Uri?GetFileUriWithDiagnostics(IBinder binder, IFileResolver fileResolver, IDiagnosticWriter diagnostics, string filePath, SyntaxBase filePathArgument)
        {
            if (!LocalModuleReference.Validate(filePath, out var validateFilePathFailureBuilder))
            {
                diagnostics.Write(validateFilePathFailureBuilder.Invoke(DiagnosticBuilder.ForPosition(filePathArgument)));
                return(null);
            }

            var fileUri = fileResolver.TryResolveFilePath(binder.FileSymbol.FileUri, filePath);

            if (fileUri is null)
            {
                diagnostics.Write(DiagnosticBuilder.ForPosition(filePathArgument).FilePathCouldNotBeResolved(filePath, binder.FileSymbol.FileUri.LocalPath));
                return(null);
            }

            if (!fileUri.IsFile)
            {
                diagnostics.Write(DiagnosticBuilder.ForPosition(filePathArgument).UnableToLoadNonFileUri(fileUri));
                return(null);
            }
            return(fileUri);
        }
コード例 #22
0
        private IEnumerable <ErrorDiagnostic> ValidateIdentifierAccess()
        {
            return(SyntaxAggregator.Aggregate(this.DeclaringParameter, new List <ErrorDiagnostic>(), (accumulated, current) =>
            {
                if (current is VariableAccessSyntax)
                {
                    Symbol?symbol = this.Context.Bindings.TryGetValue(current);

                    // excluded symbol kinds already generate errors - no need to duplicate
                    if (symbol != null &&
                        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));
        }
コード例 #23
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)
            {
                var ambiguousNamespaces = foundSymbols.OfType <FunctionSymbol>().Select(x => x.DeclaringObject.Name);

                // ambiguous symbol
                return(new ErrorSymbol(DiagnosticBuilder.ForPosition(identifierSyntax).AmbiguousSymbolReference(identifierSyntax.IdentifierName, ambiguousNamespaces.ToImmutableSortedSet(StringComparer.Ordinal))));
            }

            var foundSymbol = foundSymbols.FirstOrDefault();

            return(isFunctionCall ?
                   SymbolValidator.ResolveUnqualifiedFunction(allowedFlags, foundSymbol, identifierSyntax, namespaceResolver) :
                   SymbolValidator.ResolveUnqualifiedSymbol(foundSymbol, identifierSyntax, namespaceResolver, declarations.Keys));
        }
コード例 #24
0
ファイル: ErrorBuilderTests.cs プロジェクト: joncloud/bicep
        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;

                // 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);
            }
        }
コード例 #25
0
ファイル: TypeValidator.cs プロジェクト: swipswaps/bicep
        private static TypeSymbol NarrowArrayAssignmentType(ITypeManager typeManager, ArraySyntax expression, ArrayType targetType, IDiagnosticWriter diagnosticWriter, bool skipConstantCheck)
        {
            // if we have parse errors, no need to check assignability
            // we should not return the parse errors however because they will get double collected
            if (expression.HasParseErrors())
            {
                return(targetType);
            }

            var arrayProperties = new List <TypeSymbol>();

            foreach (var arrayItemSyntax in expression.Items)
            {
                arrayProperties.Add(NarrowTypeInternal(
                                        typeManager,
                                        arrayItemSyntax.Value,
                                        targetType.Item.Type,
                                        diagnosticWriter,
                                        (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).ArrayTypeMismatch(ShouldWarn(targetType), expectedType, actualType),
                                        skipConstantCheck,
                                        skipTypeErrors: true));
            }

            return(new TypedArrayType(UnionType.Create(arrayProperties), targetType.ValidationFlags));
        }
コード例 #26
0
        private static void GetObjectAssignmentDiagnostics(ITypeManager typeManager, ObjectSyntax expression, ObjectType targetType, IList <Diagnostic> diagnostics, bool skipConstantCheck)
        {
            // TODO: Short-circuit on any object to avoid unnecessary processing?
            // TODO: Consider doing the schema check even if there are parse errors
            // if we have parse errors, there's no point to check assignability
            // we should not return the parse errors however because they will get double collected
            if (expression.HasParseErrors())
            {
                return;
            }

            var propertyMap = expression.ToPropertyDictionary();

            var missingRequiredProperties = targetType.Properties.Values
                                            .Where(p => p.Flags.HasFlag(TypePropertyFlags.Required) && propertyMap.ContainsKey(p.Name) == false)
                                            .Select(p => p.Name)
                                            .OrderBy(p => p)
                                            .ConcatString(LanguageConstants.ListSeparator);

            if (string.IsNullOrEmpty(missingRequiredProperties) == false)
            {
                diagnostics.Add(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperties(missingRequiredProperties));
            }

            foreach (var declaredProperty in targetType.Properties.Values)
            {
                if (propertyMap.TryGetValue(declaredProperty.Name, out var declaredPropertySyntax))
                {
                    bool skipConstantCheckForProperty = skipConstantCheck;

                    // is the property marked as requiring compile-time constants and has the parent already validated this?
                    if (skipConstantCheck == false && declaredProperty.Flags.HasFlag(TypePropertyFlags.Constant))
                    {
                        // validate that values are compile-time constants
                        diagnostics.AddRange(GetCompileTimeConstantViolation(declaredPropertySyntax.Value));

                        // disable compile-time constant validation for children
                        skipConstantCheckForProperty = true;
                    }

                    if (declaredProperty.Flags.HasFlag(TypePropertyFlags.ReadOnly))
                    {
                        // the declared property is read-only
                        // value cannot be assigned to a read-only property
                        diagnostics.Add(DiagnosticBuilder.ForPosition(declaredPropertySyntax.Key).CannotAssignToReadOnlyProperty(declaredProperty.Name));
                    }

                    // declared property is specified in the value object
                    // validate type
                    GetExpressionAssignmentDiagnosticsInternal(
                        typeManager,
                        declaredPropertySyntax.Value,
                        declaredProperty.TypeReference.Type,
                        diagnostics,
                        (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).PropertyTypeMismatch(declaredProperty.Name, expectedType, actualType),
                        skipConstantCheckForProperty,
                        skipTypeErrors: true);
                }
            }

            // find properties that are specified on in the expression object but not declared in the schema
            var extraProperties = expression.Properties
                                  .Select(p => p.GetKeyText())
                                  .Except(targetType.Properties.Values.Select(p => p.Name), LanguageConstants.IdentifierComparer)
                                  .Select(name => propertyMap[name]);

            if (targetType.AdditionalPropertiesType == null)
            {
                var validUnspecifiedProperties = targetType.Properties.Values
                                                 .Where(p => !p.Flags.HasFlag(TypePropertyFlags.ReadOnly))
                                                 .Select(p => p.Name)
                                                 .Except(expression.Properties.Select(p => p.GetKeyText()), LanguageConstants.IdentifierComparer)
                                                 .OrderBy(x => x);

                // extra properties are not allowed by the type
                foreach (var extraProperty in extraProperties)
                {
                    var error = validUnspecifiedProperties.Any() ?
                                DiagnosticBuilder.ForPosition(extraProperty.Key).DisallowedPropertyWithPermissibleProperties(extraProperty.GetKeyText(), targetType.Name, validUnspecifiedProperties) :
                                DiagnosticBuilder.ForPosition(extraProperty.Key).DisallowedProperty(extraProperty.GetKeyText(), targetType.Name);

                    diagnostics.AddRange(error.AsEnumerable());
                }
            }
            else
            {
                // extra properties must be assignable to the right type
                foreach (ObjectPropertySyntax extraProperty in extraProperties)
                {
                    bool skipConstantCheckForProperty = skipConstantCheck;

                    // is the property marked as requiring compile-time constants and has the parent already validated this?
                    if (skipConstantCheckForProperty == false && targetType.AdditionalPropertiesFlags.HasFlag(TypePropertyFlags.Constant))
                    {
                        // validate that values are compile-time constants
                        diagnostics.AddRange(GetCompileTimeConstantViolation(extraProperty.Value));

                        // disable compile-time constant validation for children
                        skipConstantCheckForProperty = true;
                    }

                    GetExpressionAssignmentDiagnosticsInternal(
                        typeManager,
                        extraProperty.Value,
                        targetType.AdditionalPropertiesType.Type,
                        diagnostics,
                        (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).PropertyTypeMismatch(extraProperty.GetKeyText(), expectedType, actualType),
                        skipConstantCheckForProperty,
                        skipTypeErrors: true);
                }
            }
        }
コード例 #27
0
ファイル: TypeValidator.cs プロジェクト: swipswaps/bicep
        private static TypeSymbol NarrowDiscriminatedObjectType(ITypeManager typeManager, ObjectSyntax expression, DiscriminatedObjectType targetType, IDiagnosticWriter diagnosticWriter, bool skipConstantCheck)
        {
            // if we have parse errors, there's no point to check assignability
            // we should not return the parse errors however because they will get double collected
            if (expression.HasParseErrors())
            {
                return(LanguageConstants.Any);
            }

            var discriminatorProperty = expression.Properties.FirstOrDefault(x => LanguageConstants.IdentifierComparer.Equals(x.TryGetKeyText(), targetType.DiscriminatorKey));

            if (discriminatorProperty == null)
            {
                // object doesn't contain the discriminator field
                diagnosticWriter.Write(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperty(ShouldWarn(targetType), targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType));

                var propertyKeys = expression.Properties
                                   .Select(x => x.TryGetKeyText())
                                   .Where(key => !string.IsNullOrEmpty(key))
                                   .Select(key => key !);

                // do a reverse lookup to check if there's any misspelled discriminator key
                var misspelledDiscriminatorKey = SpellChecker.GetSpellingSuggestion(targetType.DiscriminatorKey, propertyKeys);

                if (misspelledDiscriminatorKey != null)
                {
                    diagnosticWriter.Write(DiagnosticBuilder.ForPosition(expression).DisallowedPropertyWithSuggestion(ShouldWarn(targetType), misspelledDiscriminatorKey, targetType.DiscriminatorKeysUnionType, targetType.DiscriminatorKey));
                }


                return(LanguageConstants.Any);
            }

            // At some point in the future we may want to relax the expectation of a string literal key, and allow a generic string.
            // In this case, the best we can do is validate against the union of all the settable properties.
            // Let's not do this just yet, and see if a use-case arises.

            var discriminatorType = typeManager.GetTypeInfo(discriminatorProperty.Value);

            if (!(discriminatorType is StringLiteralType stringLiteralDiscriminator))
            {
                diagnosticWriter.Write(DiagnosticBuilder.ForPosition(expression).PropertyTypeMismatch(ShouldWarn(targetType), targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType));
                return(LanguageConstants.Any);
            }

            if (!targetType.UnionMembersByKey.TryGetValue(stringLiteralDiscriminator.Name, out var selectedObjectReference))
            {
                // no matches
                var    discriminatorCandidates = targetType.UnionMembersByKey.Keys.OrderBy(x => x);
                string?suggestedDiscriminator  = SpellChecker.GetSpellingSuggestion(stringLiteralDiscriminator.Name, discriminatorCandidates);
                var    builder    = DiagnosticBuilder.ForPosition(discriminatorProperty.Value);
                bool   shouldWarn = ShouldWarn(targetType);

                diagnosticWriter.Write(suggestedDiscriminator != null
                    ? builder.PropertyStringLiteralMismatchWithSuggestion(shouldWarn, targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, stringLiteralDiscriminator.Name, suggestedDiscriminator)
                    : builder.PropertyTypeMismatch(shouldWarn, targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType));

                return(LanguageConstants.Any);
            }

            if (!(selectedObjectReference.Type is ObjectType selectedObjectType))
            {
                throw new InvalidOperationException($"Discriminated type {targetType.Name} contains non-object member");
            }

            // we have a match!
            return(NarrowObjectType(typeManager, expression, selectedObjectType, diagnosticWriter, skipConstantCheck));
        }
コード例 #28
0
        public static IEnumerable <Diagnostic> GetExpressionAssignmentDiagnostics(ITypeManager typeManager, SyntaxBase expression, TypeSymbol targetType, Func <TypeSymbol, TypeSymbol, SyntaxBase, Diagnostic>?typeMismatchErrorFactory = null)
        {
            // generic error creator if a better one was not specified.
            typeMismatchErrorFactory ??= (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).ExpectedValueTypeMismatch(expectedType.Name, actualType.Name);

            var diagnostics = new List <Diagnostic>();

            GetExpressionAssignmentDiagnosticsInternal(typeManager, expression, targetType, diagnostics, typeMismatchErrorFactory, skipConstantCheck: false, skipTypeErrors: false);

            return(diagnostics);
        }
コード例 #29
0
        private Symbol LookupSymbolByName(string name, TextSpan span, NamespaceSymbol? @namespace)
        {
            NamespaceSymbol?FindNamespace(string name)
            {
                this.namespaces.TryGetValue(name, out NamespaceSymbol @namespace);
                return(@namespace);
            }

            Symbol?foundSymbol;

            if (@namespace == null)
            {
                // attempt to find name in the imported namespaces
                var namespaceSymbol = FindNamespace(name);

                if (namespaceSymbol != null)
                {
                    // namespace symbol found
                    return(ValidateFunctionFlags(namespaceSymbol, span));
                }

                // 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(name, out var localSymbol))
                {
                    // we found the symbol in the local namespace
                    return(ValidateFunctionFlags(localSymbol, span));
                }

                // attempt to find function in all imported namespaces
                var foundSymbols = this.namespaces
                                   .Select(kvp => kvp.Value.TryGetFunctionSymbol(name))
                                   .Where(symbol => symbol != null)
                                   .ToList();

                if (foundSymbols.Count() > 1)
                {
                    // ambiguous symbol
                    return(new UnassignableSymbol(DiagnosticBuilder.ForPosition(span)
                                                  .AmbiguousSymbolReference(name, this.namespaces.Keys)));
                }

                foundSymbol = foundSymbols.FirstOrDefault();

                if (foundSymbol == null)
                {
                    var nameCandidates = this.declarations.Values
                                         .Concat(this.namespaces.SelectMany(kvp => kvp.Value.Descendants))
                                         .Select(symbol => symbol.Name)
                                         .ToImmutableSortedSet();

                    var suggestedName = SpellChecker.GetSpellingSuggestion(name, nameCandidates);

                    return(suggestedName != null
                        ? new UnassignableSymbol(DiagnosticBuilder.ForPosition(span).SymbolicNameDoesNotExistWithSuggestion(name, suggestedName))
                        : new UnassignableSymbol(DiagnosticBuilder.ForPosition(span).SymbolicNameDoesNotExist(name)));
                }
            }
            else
            {
                foundSymbol = @namespace.TryGetFunctionSymbol(name);

                if (foundSymbol == null)
                {
                    return(new UnassignableSymbol(DiagnosticBuilder.ForPosition(span).FunctionNotFound(name, @namespace.Name)));
                }
            }

            return(ValidateFunctionFlags(foundSymbol, span));
        }
コード例 #30
0
ファイル: TypeValidator.cs プロジェクト: swipswaps/bicep
        private static TypeSymbol NarrowObjectType(ITypeManager typeManager, ObjectSyntax expression, ObjectType targetType, IDiagnosticWriter diagnosticWriter, bool skipConstantCheck)
        {
            // TODO: Short-circuit on any object to avoid unnecessary processing?
            // TODO: Consider doing the schema check even if there are parse errors
            // if we have parse errors, there's no point to check assignability
            // we should not return the parse errors however because they will get double collected
            if (expression.HasParseErrors())
            {
                return(targetType);
            }

            var namedPropertyMap = expression.ToNamedPropertyDictionary();

            var missingRequiredProperties = targetType.Properties.Values
                                            .Where(p => p.Flags.HasFlag(TypePropertyFlags.Required) && !namedPropertyMap.ContainsKey(p.Name))
                                            .Select(p => p.Name)
                                            .OrderBy(p => p);

            if (missingRequiredProperties.Any())
            {
                IPositionable positionable = expression;
                string        blockName    = "object";

                var parent = typeManager.GetParent(expression);
                if (parent is ObjectPropertySyntax objectPropertyParent)
                {
                    positionable = objectPropertyParent.Key;
                    blockName    = "object";
                }
                else if (parent is INamedDeclarationSyntax declarationParent)
                {
                    positionable = declarationParent.Name;
                    blockName    = declarationParent.Keyword.Text;
                }

                diagnosticWriter.Write(DiagnosticBuilder.ForPosition(positionable).MissingRequiredProperties(ShouldWarn(targetType), missingRequiredProperties, blockName));
            }

            var narrowedProperties = new List <TypeProperty>();

            foreach (var declaredProperty in targetType.Properties.Values)
            {
                if (namedPropertyMap.TryGetValue(declaredProperty.Name, out var declaredPropertySyntax))
                {
                    bool skipConstantCheckForProperty = skipConstantCheck;

                    // is the property marked as requiring compile-time constants and has the parent already validated this?
                    if (skipConstantCheck == false && declaredProperty.Flags.HasFlag(TypePropertyFlags.Constant))
                    {
                        // validate that values are compile-time constants
                        GetCompileTimeConstantViolation(declaredPropertySyntax.Value, diagnosticWriter);

                        // disable compile-time constant validation for children
                        skipConstantCheckForProperty = true;
                    }

                    if (declaredProperty.Flags.HasFlag(TypePropertyFlags.ReadOnly))
                    {
                        // the declared property is read-only
                        // value cannot be assigned to a read-only property
                        diagnosticWriter.Write(DiagnosticBuilder.ForPosition(declaredPropertySyntax.Key).CannotAssignToReadOnlyProperty(ShouldWarn(targetType), declaredProperty.Name));
                        narrowedProperties.Add(new TypeProperty(declaredProperty.Name, declaredProperty.TypeReference.Type, declaredProperty.Flags));
                        continue;
                    }

                    // declared property is specified in the value object
                    // validate type
                    var narrowedType = NarrowTypeInternal(
                        typeManager,
                        declaredPropertySyntax.Value,
                        declaredProperty.TypeReference.Type,
                        diagnosticWriter,
                        GetPropertyMismatchErrorFactory(ShouldWarn(targetType), declaredProperty.Name),
                        skipConstantCheckForProperty,
                        skipTypeErrors: true);

                    narrowedProperties.Add(new TypeProperty(declaredProperty.Name, narrowedType, declaredProperty.Flags));
                }
                else
                {
                    narrowedProperties.Add(declaredProperty);
                }
            }

            // find properties that are specified on in the expression object but not declared in the schema
            var extraProperties = expression.Properties
                                  .Where(p => !(p.TryGetKeyText() is string keyName) || !targetType.Properties.ContainsKey(keyName));

            if (targetType.AdditionalPropertiesType == null)
            {
                bool shouldWarn = ShouldWarn(targetType);
                var  validUnspecifiedProperties = targetType.Properties.Values
                                                  .Where(p => !p.Flags.HasFlag(TypePropertyFlags.ReadOnly) && !namedPropertyMap.ContainsKey(p.Name))
                                                  .Select(p => p.Name)
                                                  .OrderBy(x => x);

                // extra properties are not allowed by the type
                foreach (var extraProperty in extraProperties)
                {
                    Diagnostic error;
                    var        builder = DiagnosticBuilder.ForPosition(extraProperty.Key);

                    if (extraProperty.TryGetKeyText() is string keyName)
                    {
                        error = validUnspecifiedProperties.Any() switch
                        {
                            true => SpellChecker.GetSpellingSuggestion(keyName, validUnspecifiedProperties) switch
                            {
                                string suggestedKeyName when suggestedKeyName != null
                                => builder.DisallowedPropertyWithSuggestion(shouldWarn, keyName, targetType, suggestedKeyName),
                                _ => builder.DisallowedPropertyWithPermissibleProperties(shouldWarn, keyName, targetType, validUnspecifiedProperties)
                            },