Пример #1
0
        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));
        }
Пример #2
0
        private static void GetDiscriminatedObjectAssignmentDiagnostics(ITypeManager typeManager, ObjectSyntax expression, DiscriminatedObjectType targetType, IList <Diagnostic> diagnostics, 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;
            }

            var propertyMap = expression.ToPropertyDictionary();

            if (!propertyMap.TryGetValue(targetType.DiscriminatorKey, out var discriminatorProperty))
            {
                // object doesn't contain the discriminator field
                diagnostics.Add(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperty(targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType));
                return;
            }

            // 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))
            {
                diagnostics.Add(DiagnosticBuilder.ForPosition(expression).PropertyTypeMismatch(targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType));
                return;
            }

            if (!targetType.UnionMembersByKey.TryGetValue(stringLiteralDiscriminator.Name, out var selectedObjectReference))
            {
                // no matches
                diagnostics.Add(DiagnosticBuilder.ForPosition(discriminatorProperty.Value).PropertyTypeMismatch(targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType));
                return;
            }

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

            // we have a match!
            GetObjectAssignmentDiagnostics(typeManager, expression, selectedObjectType, diagnostics, skipConstantCheck);
        }
Пример #3
0
        private static IEnumerable <ErrorDiagnostic> GetDiscriminatedObjectAssignmentDiagnostics(ITypeManager typeManager, ObjectSyntax expression, DiscriminatedObjectType targetType, 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())
            {
                yield break;
            }

            var propertyMap = expression.ToPropertyDictionary();

            if (!propertyMap.TryGetValue(targetType.DiscriminatorKey, out var discriminatorProperty))
            {
                // object doesn't contain the discriminator field
                yield return(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperty(targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType));

                yield break;
            }

            // 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, new TypeManagerContext());

            if (!(discriminatorType is StringLiteralType stringLiteralDiscriminator))
            {
                yield return(DiagnosticBuilder.ForPosition(expression).PropertyTypeMismatch(targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType));

                yield break;
            }

            if (!targetType.UnionMembersByKey.TryGetValue(stringLiteralDiscriminator.Name, out var selectedObjectType))
            {
                // no matches
                yield return(DiagnosticBuilder.ForPosition(discriminatorProperty.Value).PropertyTypeMismatch(targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType));

                yield break;
            }

            // we have a match!
            foreach (var diagnostic in GetObjectAssignmentDiagnostics(typeManager, expression, selectedObjectType, skipConstantCheck))
            {
                yield return(diagnostic);
            }
            yield break;
        }