@@ -848,8 +848,15 @@ checkFormulas env beh (ProdCons (fp, ep) (fc, ec)) =
848848 (DNF pss, DNF css) -> F.for_ pss $ \(Conjunct ps) -> do
849849 anyOfAt
850850 beh'
851− (NoMatchingCondition $ SomeCondition <$> ps)
851+ (issueFromConjunct ps)
852852 [F.for_ cs $ checkImplication env beh' ps | Conjunct cs <- S.toList css]
853+ where
854+ issueFromConjunct :: Typeable t => [Condition t] -> Issue 'TypedSchemaLevel
855+ issueFromConjunct ps
856+ | Just e <- findExactly ps
857+ , all (satisfiesTyped e) ps =
858+ EnumDoesntSatisfy $ untypeValue e
859+ issueFromConjunct ps = NoMatchingCondition $ SomeCondition <$> ps
853860
854861checkContradiction
855862 :: Behavior 'TypedSchemaLevel
@@ -943,18 +950,20 @@ checkImplication env beh prods cons = case findExactly prods of
943950 anyOfAt beh NoMatchingProperties $
944951 NE.toList pm <&> \(props', madd') -> do
945952 F.for_ (S.fromList $ M.keys props <> M.keys props') $ \k -> do
946− let go sch sch' = checkCompatibility env (beh >>> step (InProperty k)) (ProdCons sch sch')
947− case (M.lookup k props', madd', M.lookup k props, madd) of
948− (Nothing, Nothing, _, _) -> pure () -- vacuously
949− (_, _, Nothing, Nothing) -> issueAt beh (UnexpectedProperty k)
950− (Just p', _, Just p, _) -> go (propRefSchema p') (propRefSchema p)
951− (Nothing, Just add', Just p, _) -> go add' (propRefSchema p)
952− (Just p', _, Nothing, Just add) -> go (propRefSchema p') add
953− (Nothing, Just _, Nothing, Just _) -> pure ()
954953 case (maybe False propRequired $ M.lookup k props', maybe False propRequired $ M.lookup k props) of
954+ -- producer does not require field, but consumer does (can fail)
955955 (False, True) -> issueAt beh (PropertyNowRequired k)
956− _ -> pure ()
957− pure ()
956+ _ -> do
957+ let go sch sch' = checkCompatibility env (beh >>> step (InProperty k)) (ProdCons sch sch')
958+ case (M.lookup k props', madd', M.lookup k props, madd) of
959+ -- (producer, additional producer, consumer, additional consumer)
960+ (Nothing, Nothing, _, _) -> pure () -- vacuously
961+ -- this ^ seems fishy. Producer does not produce anything, but consumer might expect a field.
962+ (_, _, Nothing, Nothing) -> issueAt beh (UnexpectedProperty k)
963+ (Just p', _, Just p, _) -> go (propRefSchema p') (propRefSchema p)
964+ (Nothing, Just add', Just p, _) -> go add' (propRefSchema p)
965+ (Just p', _, Nothing, Just add) -> go (propRefSchema p') add
966+ (Nothing, Just _, Nothing, Just _) -> pure ()
958967 case (madd', madd) of
959968 (Nothing, _) -> pure () -- vacuously
960969 (_, Nothing) -> issueAt beh NoAdditionalProperties
@@ -974,14 +983,16 @@ checkImplication env beh prods cons = case findExactly prods of
974983 else issueAt beh (MatchingMinPropertiesWeak ProdCons {producer = m', consumer = m})
975984 Nothing -> issueAt beh (NoMatchingMinProperties m)
976985 where
977− findExactly (Exactly x : _) = Just x
978− findExactly (_ : xs) = findExactly xs
979− findExactly [] = Nothing
980986 findRelevant combine extr =
981987 fmap (foldr1 combine) . NE.nonEmpty . mapMaybe extr
982988 lcmScientific (toRational -> a) (toRational -> b) =
983989 fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
984990
991+findExactly :: [Condition t] -> Maybe (TypedValue t)
992+findExactly (Exactly x : _) = Just x
993+findExactly (_ : xs) = findExactly xs
994+findExactly [] = Nothing
995+
985996instance Issuable 'TypedSchemaLevel where
986997 data Issue 'TypedSchemaLevel
987998 = -- | producer produces a specific value ($1), consumer has a condition that is not satisfied by said value
@@ -1010,7 +1021,7 @@ instance Issuable 'TypedSchemaLevel where
10101021 NoMatchingMinLength Integer
10111022 | -- | consumer declares a minimum length of the string ($1), producer declares a weaker (lower) limit ($2)
10121023 MatchingMinLengthWeak (ProdCons Integer)
1013− | -- | consumer declares the string value must matrix a regex ($1), producer doesn't declare or declares different regex (TODO: #32)
1024+ | -- | consumer declares the string value must match a regex ($1), producer doesn't declare or declares different regex (TODO: #32)
10141025 NoMatchingPattern Pattern
10151026 | -- | consumer declares the items of an array must satisfy some condition, producer doesn't
10161027 NoMatchingItems
@@ -1040,44 +1051,44 @@ instance Issuable 'TypedSchemaLevel where
10401051 NoMatchingMinProperties Integer
10411052 | -- | consumer declares a minimum number of properties in the object ($1), producer declares a weaker (lower) limit ($2)
10421053 MatchingMinPropertiesWeak (ProdCons Integer)
1043− | -- | consumer declares that the value must satisfy a disjunction of some conditions, but producer's requirements couldn't be matched against any single one of them (TODO: split heuristic #71)
1054+ | -- | producer declares that the value must satisfy a disjunction of some conditions, but consumer's requirements couldn't be matched against any single one of them (TODO: split heuristic #71)
10441055 NoMatchingCondition [SomeCondition]
10451056 | -- | consumer indicates that values of this type are now allowed, but the producer does not do so (currently we only check immediate contradictions, c.f. #70)
10461057 NoContradiction
10471058 deriving stock (Eq, Ord, Show)
10481059 issueIsUnsupported _ = False
1049− describeIssue (EnumDoesntSatisfy v) = para "The following enum value will yield an error:" <> showJSONValue v
1050− describeIssue (NoMatchingEnum v) = para "The following enum value is not supported:" <> showJSONValue v
1051− describeIssue (NoMatchingMaximum b) = para $ "Unexpected upper bound " <> showBound b <> "."
1052− describeIssue (MatchingMaximumWeak (ProdCons p c)) = para $ "Expected upper bound " <> showBound p <> " but but found " <> showBound c <> "."
1053− describeIssue (NoMatchingMinimum b) = para $ "Unexpected lower bound " <> showBound b <> "."
1054− describeIssue (MatchingMinimumWeak (ProdCons p c)) = para $ "Expected lower bound " <> showBound p <> " but but found " <> showBound c <> "."
1055− describeIssue (NoMatchingMultipleOf n) = para $ "Didn't expect the value to be a multiple of " <> show' n <> " but it was."
1056− describeIssue (MatchingMultipleOfWeak (ProdCons p c)) = para $ "Expected the value to be a multiple of " <> show' p <> " but found a multiple of " <> show' c <> "."
1057− describeIssue (NoMatchingFormat f) = para $ "Unexpected format: " <> code f <> "."
1058− describeIssue (NoMatchingMaxLength n) = para $ "Unexpected maximum length of the string " <> show' n <> "."
1059− describeIssue (MatchingMaxLengthWeak (ProdCons p c)) = para $ "Expected the maximum length of the string to be " <> show' p <> "but it was " <> show' c <> "."
1060− describeIssue (NoMatchingMinLength n) = para $ "Unexpected minimum length of the string " <> show' n <> "."
1061− describeIssue (MatchingMinLengthWeak (ProdCons p c)) = para $ "Expected the minimum length of the string to be " <> show' p <> "but it was " <> show' c <> "."
1062− describeIssue (NoMatchingPattern p) = para "Unexpected pattern (regular expression): " <> codeBlock p
1063− describeIssue NoMatchingItems = para "Couldn't find any matching items."
1064− describeIssue (NoMatchingMaxItems n) = para $ "Unexpected maximum length of the array " <> show' n <> "."
1065− describeIssue (MatchingMaxItemsWeak (ProdCons p c)) = para $ "Expected the maximum length of the array to be " <> show' p <> "but it was " <> show' c <> "."
1066− describeIssue (NoMatchingMinItems n) = para $ "Unexpected minimum length of the array " <> show' n <> "."
1067− describeIssue (MatchingMinItemsWeak (ProdCons p c)) = para $ "Expected the minimum length of the array to be " <> show' p <> "but it was " <> show' c <> "."
1068− describeIssue NoMatchingUniqueItems = para "Didn't expect the items to be unique, but they were."
1069− describeIssue NoMatchingProperties = para "Couldn't find matching properties."
1070− describeIssue (UnexpectedProperty p) = para $ "Expected the property " <> code p <> " to be allowed, but it wasn't."
1071− describeIssue (PropertyNowRequired p) = para $ "Don't have a required property " <> code p <> "."
1072− describeIssue NoAdditionalProperties = para "Expected additional properties to be allowed, but they weren't."
1073− describeIssue (NoMatchingMaxProperties n) = para $ "Unexpected maximum number of properties " <> show' n <> "."
1074− describeIssue (MatchingMaxPropertiesWeak (ProdCons p c)) = para $ "Expected the maximum number of properties to be " <> show' p <> "but it was " <> show' c <> "."
1075− describeIssue (NoMatchingMinProperties n) = para $ "Unexpected minimum number of properties " <> show' n <> "."
1076− describeIssue (MatchingMinPropertiesWeak (ProdCons p c)) = para $ "Expected the minimum number of properties to be " <> show' p <> "but it was " <> show' c <> "."
1060+ describeIssue (EnumDoesntSatisfy v) = para "The following enum value was added:" <> showJSONValue v
1061+ describeIssue (NoMatchingEnum v) = para "The following enum value has been removed:" <> showJSONValue v
1062+ describeIssue (NoMatchingMaximum b) = para $ "Upper bound has been added:" <> showBound b <> "."
1063+ describeIssue (MatchingMaximumWeak (ProdCons p c)) = para $ "Upper bound changed from " <> showBound p <> " to " <> showBound c <> "."
1064+ describeIssue (NoMatchingMinimum b) = para $ "Lower bound has been added: " <> showBound b <> "."
1065+ describeIssue (MatchingMinimumWeak (ProdCons p c)) = para $ "Lower bound changed from " <> showBound p <> " to " <> showBound c <> "."
1066+ describeIssue (NoMatchingMultipleOf n) = para $ "Value is now a multiple of " <> show' n <> "."
1067+ describeIssue (MatchingMultipleOfWeak (ProdCons p c)) = para $ "Value changed from being a multiple of " <> show' p <> " to being a multiple of " <> show' c <> "."
1068+ describeIssue (NoMatchingFormat f) = para $ "Format added: " <> code f <> "."
1069+ describeIssue (NoMatchingMaxLength n) = para $ "Maximum length added: " <> show' n <> "."
1070+ describeIssue (MatchingMaxLengthWeak (ProdCons p c)) = para $ "Maximum length of the string changed from " <> show' p <> " to " <> show' c <> "."
1071+ describeIssue (NoMatchingMinLength n) = para $ "Minimum length of the string added: " <> show' n <> "."
1072+ describeIssue (MatchingMinLengthWeak (ProdCons p c)) = para $ "Minimum length of the string changed from " <> show' p <> " to " <> show' c <> "."
1073+ describeIssue (NoMatchingPattern p) = para "Pattern (regular expression) added: " <> codeBlock p
1074+ describeIssue NoMatchingItems = para "Array item schema has been added."
1075+ describeIssue (NoMatchingMaxItems n) = para $ "Maximum length of the array has been added " <> show' n <> "."
1076+ describeIssue (MatchingMaxItemsWeak (ProdCons p c)) = para $ "Maximum length of the array changed from " <> show' p <> " to " <> show' c <> "."
1077+ describeIssue (NoMatchingMinItems n) = para $ "Minimum length of the array added: " <> show' n <> "."
1078+ describeIssue (MatchingMinItemsWeak (ProdCons p c)) = para $ "Minimum length of the array changed from " <> show' p <> " to " <> show' c <> "."
1079+ describeIssue NoMatchingUniqueItems = para "Items are now required to be unique."
1080+ describeIssue NoMatchingProperties = para "Property added."
1081+ describeIssue (UnexpectedProperty p) = para $ "Property " <> code p <> " has been removed."
1082+ describeIssue (PropertyNowRequired p) = para $ "Property " <> code p <> " has become required."
1083+ describeIssue NoAdditionalProperties = para "Additional properties have been removed."
1084+ describeIssue (NoMatchingMaxProperties n) = para $ "Maximum number of properties has been added: " <> show' n <> "."
1085+ describeIssue (MatchingMaxPropertiesWeak (ProdCons p c)) = para $ "Maximum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
1086+ describeIssue (NoMatchingMinProperties n) = para $ "Minimum number of properties added: " <> show' n <> "."
1087+ describeIssue (MatchingMinPropertiesWeak (ProdCons p c)) = para $ "Minimum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
10771088 describeIssue (NoMatchingCondition conds) =
1078− para "Expected the following conditions to hold, but they didn't:"
1089+ para "Expected the following conditions to hold, but they didn't (please file a bug if you see this):"
10791090 <> bulletList ((\(SomeCondition c) -> showCondition c) <$> conds)
1080− describeIssue NoContradiction = para "Expected the type to be allowed, but it wasn't."
1091+ describeIssue NoContradiction = para "The type has been removed."
10811092
10821093showJSONValue :: A.Value -> Blocks
10831094showJSONValue v = codeBlockWith ("", ["json"], mempty) (T.decodeUtf8 . BSL.toStrict . A.encode $ v)