@@ -843,10 +843,18 @@ checkFormulas env beh (ProdCons (fp, ep) (fc, ec)) =
843843 -- (⋃_i ⋂_j A[i,j]) ⊂ ∅ = /\_i (⋂_j A[i,j]) ⊂ ∅
844844 -- where we again delegate (⋂_j A[j]) ⊂ ∅ to a heuristic, though here the
845845 -- shortcut of \/_j A[j] ⊂ ∅ hardly helps.
846+ let typesRestricted = not (anyBottomTypes fp) && anyBottomTypes fc
847+ -- Specifically handle the case when a schema's type has been
848+ -- restricted from "all" to specific types: if all types were allowed
849+ -- in the producer and not all types are allowed in the consumer, it's
850+ -- usually easier to say what's left than what's removed
851+ when typesRestricted $ issueAt beh $ TypesRestricted $ nonBottomTypes fc
846852 forType_ $ \tyName ty -> do
847853 let beh' = beh >>> step (OfType tyName)
848854 case (ty fp, ty fc) of
849− (DNF pss, BottomFormula) -> F.for_ pss $ \(Conjunct ps) -> checkContradiction beh' ps
855+ (DNF pss, BottomFormula) -> unless typesRestricted $ do
856+ -- don't repeat the TypesRestricted issue
857+ F.for_ pss $ \(Conjunct ps) -> checkContradiction beh' ps
850858 (DNF pss, SingleConjunct cs) -> F.for_ pss $ \(Conjunct ps) -> do
851859 F.for_ cs $ checkImplication env beh' ps -- avoid disjunction if there's only one conjunct
852860 (TopFormula, DNF css) ->
@@ -859,7 +867,15 @@ checkFormulas env beh (ProdCons (fp, ep) (fc, ec)) =
859867 beh'
860868 (issueFromConjunct ps)
861869 [F.for_ cs $ checkImplication env beh' ps | Conjunct cs <- S.toList css]
870+ pure ()
862871 where
872+ anyBottomTypes f = getAny $
873+ foldType $ \_ ty -> case ty f of
874+ BottomFormula -> Any True
875+ _ -> mempty
876+ nonBottomTypes f = foldType $ \tyName ty -> case ty f of
877+ BottomFormula -> mempty
878+ _ -> [tyName]
863879 issueFromConjunct :: Typeable t => [Condition t] -> Issue 'TypedSchemaLevel
864880 issueFromConjunct ps
865881 | Just e <- findExactly ps
@@ -959,20 +975,26 @@ checkImplication env beh prods cons = case findExactly prods of
959975 anyOfAt beh NoMatchingProperties $
960976 NE.toList pm <&> \(props', madd') -> do
961977 F.for_ (S.fromList $ M.keys props <> M.keys props') $ \k -> do
978+ let beh' = beh >>> step (InProperty k)
979+ go sch sch' = checkCompatibility beh' env (ProdCons sch sch')
962980 case (maybe False propRequired $ M.lookup k props', maybe False propRequired $ M.lookup k props) of
963981 -- producer does not require field, but consumer does (can fail)
964− (False, True) -> issueAt beh (PropertyNowRequired k)
965− _ -> do
966− let go sch sch' = checkCompatibility (beh >>> step (InProperty k)) env (ProdCons sch sch')
967− case (M.lookup k props', madd', M.lookup k props, madd) of
968− -- (producer, additional producer, consumer, additional consumer)
969− (Nothing, Nothing, _, _) -> pure () -- vacuously
970− -- this ^ seems fishy. Producer does not produce anything, but consumer might expect a field.
971− (_, _, Nothing, Nothing) -> issueAt beh (UnexpectedProperty k)
972− (Just p', _, Just p, _) -> go (propRefSchema p') (propRefSchema p)
973− (Nothing, Just add', Just p, _) -> go add' (propRefSchema p)
974− (Just p', _, Nothing, Just add) -> go (propRefSchema p') add
975− (Nothing, Just _, Nothing, Just _) -> pure ()
982+ (False, True) -> issueAt beh' PropertyNowRequired
983+ _ -> pure ()
984+ case (M.lookup k props', madd', M.lookup k props, madd) of
985+ -- (producer, additional producer, consumer, additional consumer)
986+ (Nothing, Nothing, _, _) -> pure () -- vacuously: the producer asserts that this field cannot exist,
987+ -- and the consumer either doesn't require it, or it does and we've already raised an error about it.
988+ (_, _, Nothing, Nothing) -> issueAt beh' UnexpectedProperty
989+ (Just p', _, Just p, _) -> go (propRefSchema p') (propRefSchema p)
990+ (Nothing, Just add', Just p, _) ->
991+ clarifyIssue (AnItem beh' (anIssue AdditionalToProperty)) $
992+ go add' (propRefSchema p)
993+ (Just p', _, Nothing, Just add) ->
994+ clarifyIssue (AnItem beh' (anIssue PropertyToAdditional)) $
995+ go (propRefSchema p') add
996+ (Nothing, Just _, Nothing, Just _) -> pure ()
997+ pure ()
976998 case (madd', madd) of
977999 (Nothing, _) -> pure () -- vacuously
9781000 (_, Nothing) -> issueAt beh NoAdditionalProperties
@@ -1046,10 +1068,6 @@ instance Issuable 'TypedSchemaLevel where
10461068 NoMatchingUniqueItems
10471069 | -- | consumer declares the properties of an object must satisfy some condition, producer doesn't
10481070 NoMatchingProperties
1049− | -- | producer allows a property that is not allowed in the consumer
1050− UnexpectedProperty Text
1051− | -- | consumer requires a property that is not required/allowed in the producer
1052− PropertyNowRequired Text
10531071 | -- | producer allows additional properties, consumer doesn't
10541072 NoAdditionalProperties
10551073 | -- | consumer declares a maximum number of properties in the object ($1), producer doesn't.
@@ -1102,18 +1120,14 @@ instance Issuable 'TypedSchemaLevel where
11021120 describeIssue Backward NoMatchingUniqueItems = para "Items are no longer required to be unique."
11031121 describeIssue Forward NoMatchingProperties = para "Property added."
11041122 describeIssue Backward NoMatchingProperties = para "Property removed."
1105− describeIssue Forward (UnexpectedProperty p) = para $ "Property " <> code p <> " has been removed."
1106− describeIssue Backward (UnexpectedProperty p) = para $ "Property " <> code p <> " has been added."
1107− describeIssue Forward (PropertyNowRequired p) = para $ "Property " <> code p <> " has become required."
1108− describeIssue Backward (PropertyNowRequired p) = para $ "Property " <> code p <> " may not be present."
11091123 describeIssue Forward NoAdditionalProperties = para "Additional properties have been removed."
11101124 describeIssue Backward NoAdditionalProperties = para "Additional properties have been added."
11111125 describeIssue Forward (NoMatchingMaxProperties n) = para $ "Maximum number of properties has been added: " <> show' n <> "."
11121126 describeIssue Backward (NoMatchingMaxProperties n) = para $ "Maximum number of properties has been removed: " <> show' n <> "."
1113− describeIssue _ (MatchingMaxPropertiesWeak (ProdCons p c)) = para $ "Maximum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
1127+ describeIssue _ (MatchingMaxPropertiesWeak (ProdCons p c)) = para $ "Maximum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
11141128 describeIssue Forward (NoMatchingMinProperties n) = para $ "Minimum number of properties added: " <> show' n <> "."
11151129 describeIssue Backward (NoMatchingMinProperties n) = para $ "Minimum number of properties removed: " <> show' n <> "."
1116− describeIssue _ (MatchingMinPropertiesWeak (ProdCons p c)) = para $ "Minimum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
1130+ describeIssue _ (MatchingMinPropertiesWeak (ProdCons p c)) = para $ "Minimum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
11171131 describeIssue _ (NoMatchingCondition conds) =
11181132 para "Expected the following conditions to hold, but they didn't (please file a bug if you see this):"
11191133 <> bulletList ((\(SomeCondition c) -> showCondition c) <$> conds)
@@ -1138,8 +1152,22 @@ instance Issuable 'SchemaLevel where
11381152 InvalidSchema Text
11391153 | -- | The schema contains a reference loop along "anyOf"/"allOf"/"oneOf".
11401154 UnguardedRecursion
1155+ | -- | Producer doesn't place any restrictions on the types, but the consumer does. List what types remain available in the consumer.
1156+ TypesRestricted [JsonType]
1157+ | -- | in the producer this field used to be handled as part of "additionalProperties", and the consumer this is a specific "properties" entry. Only thrown when this change actually causes other issues
1158+ AdditionalToProperty
1159+ | -- | in the consumer this field used to be handled as part of "additionalProperties", and the producer this is a specific "properties" entry. Only thrown when this change actually causes other issues
1160+ PropertyToAdditional
1161+ | -- | consumer requires a property that is not required/allowed in the producer
1162+ PropertyNowRequired
1163+ | -- | producer allows a property that is not allowed in the consumer
1164+ UnexpectedProperty
11411165 deriving stock (Eq, Ord, Show)
1142− issueIsUnsupported _ = True
1166+ issueIsUnsupported = \case
1167+ NotSupported _ -> True
1168+ InvalidSchema _ -> True
1169+ UnguardedRecursion -> True
1170+ _ -> False
11431171
11441172 describeIssue _ (NotSupported i) =
11451173 para (emph "Encountered a feature that OpenApi Diff does not support: " <> text i <> ".")
@@ -1147,6 +1175,20 @@ instance Issuable 'SchemaLevel where
11471175 para (emph "The schema is invalid: " <> text i <> ".")
11481176 describeIssue _ UnguardedRecursion =
11491177 para "Encountered recursion that is too complex for OpenApi Diff to untangle."
1178+ describeIssue Forward (TypesRestricted tys) = case tys of
1179+ [] -> para "No longer has any valid values." -- weird
1180+ _ -> para "Values are now limited to the following types: " <> bulletList (para . describeJSONType <$> tys)
1181+ describeIssue Backward (TypesRestricted tys) = case tys of
1182+ [] -> para "Any value of any type is now allowed." -- weird
1183+ _ -> para "Values are no longer limited to the following types: " <> bulletList (para . describeJSONType <$> tys)
1184+ describeIssue Forward AdditionalToProperty = para "The property was previously implicitly described by the catch-all \"additional properties\" case. It is now explicitly defined."
1185+ describeIssue Backward AdditionalToProperty = para "The property was previously explicitly defined. It is now implicitly described by the catch-all \"additional properties\" case."
1186+ describeIssue Forward PropertyToAdditional = para "The property was previously explicitly defined. It is now implicitly described by the catch-all \"additional properties\" case."
1187+ describeIssue Backward PropertyToAdditional = para "The property was previously implicitly described by the catch-all \"additional properties\" case. It is now explicitly defined."
1188+ describeIssue Forward PropertyNowRequired = para "The property has become required."
1189+ describeIssue Backward PropertyNowRequired = para "The property may not be present."
1190+ describeIssue Forward UnexpectedProperty = para "The property has been removed."
1191+ describeIssue Backward UnexpectedProperty = para "The property has been added."
11501192
11511193instance Behavable 'SchemaLevel 'TypedSchemaLevel where
11521194 data Behave 'SchemaLevel 'TypedSchemaLevel