Skip to content

Commit e98bc7f

Browse files
authored
Merge pull request #10554 from cabalism/fix/check-version-bounds
Additional version bound checks
2 parents ed1e4d7 + d46f325 commit e98bc7f

File tree

30 files changed

+302
-57
lines changed

30 files changed

+302
-57
lines changed

Cabal-syntax/src/Distribution/Types/VersionRange.hs

Lines changed: 86 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
1+
{-# LANGUAGE LambdaCase #-}
2+
{-# LANGUAGE ViewPatterns #-}
3+
14
module Distribution.Types.VersionRange
2-
( -- * Version ranges
5+
( -- * Version Range
36
VersionRange
47

8+
-- ** Predicates
9+
-- $predicate-examples
10+
11+
-- *** Lower Bound
12+
, hasLowerBound
13+
, hasGTLowerBound
14+
15+
-- *** Upper Bound
16+
, hasUpperBound
17+
, hasLEUpperBound
18+
, hasTrailingZeroUpperBound
19+
20+
-- *** Any Version
21+
, isAnyVersion
22+
, isAnyVersionLight
23+
524
-- ** Constructing
625
, anyVersion
726
, noVersion
@@ -16,32 +35,31 @@ module Distribution.Types.VersionRange
1635
, withinVersion
1736
, majorBoundVersion
1837

19-
-- ** Inspection
38+
-- ** Modification
39+
, normaliseVersionRange
40+
, stripParensVersionRange
2041

21-
--
22-
-- See "Distribution.Version" for more utilities.
42+
-- ** Inspection
2343
, withinRange
2444
, foldVersionRange
25-
, normaliseVersionRange
26-
, stripParensVersionRange
27-
, hasUpperBound
28-
, hasLowerBound
2945

30-
-- ** Cata & ana
46+
-- ** Parser
47+
, versionRangeParser
48+
49+
-- * Version F-Algebra
3150
, VersionRangeF (..)
51+
, projectVersionRange
52+
, embedVersionRange
3253
, cataVersionRange
3354
, anaVersionRange
3455
, hyloVersionRange
35-
, projectVersionRange
36-
, embedVersionRange
3756

38-
-- ** Utilities
39-
, isAnyVersion
40-
, isAnyVersionLight
57+
-- * Version Utilities
58+
59+
-- See "Distribution.Version" for more utilities.
4160
, wildcardUpperBound
4261
, majorUpperBound
4362
, isWildcardRange
44-
, versionRangeParser
4563
) where
4664

4765
import Distribution.Compat.Prelude
@@ -172,6 +190,9 @@ isWildcardRange ver1 ver2 = check (versionNumbers ver1) (versionNumbers ver2)
172190
-- | Does the version range have an upper bound?
173191
--
174192
-- @since 1.24.0.0
193+
--
194+
-- >>> forM ["< 1", ">= 0 && < 1", ">= 0 || < 1", "^>= 4.20.0.0"] (fmap hasUpperBound . simpleParsec)
195+
-- Just [True,True,False,True]
175196
hasUpperBound :: VersionRange -> Bool
176197
hasUpperBound =
177198
foldVersionRange
@@ -188,6 +209,9 @@ hasUpperBound =
188209
-- the implicit >=0 lower bound.
189210
--
190211
-- @since 1.24.0.0
212+
--
213+
-- >>> forM ["< 1", ">= 0 && < 1", ">= 0 || < 1", "^>= 4.20.0.0"] (fmap hasLowerBound . simpleParsec)
214+
-- Just [False,True,False,True]
191215
hasLowerBound :: VersionRange -> Bool
192216
hasLowerBound =
193217
foldVersionRange
@@ -197,3 +221,50 @@ hasLowerBound =
197221
(const False)
198222
(&&)
199223
(||)
224+
225+
-- | Is the upper bound version range (less than or equal (LE, <=)?
226+
--
227+
-- >>> forM ["< 1", "<= 1", ">= 0 && < 1", ">= 0 || < 1", ">= 0 && <= 1", ">= 0 || <= 1", "^>= 4.20.0.0"] (fmap hasLEUpperBound . simpleParsec)
228+
-- Just [False,True,False,False,True,True,False]
229+
hasLEUpperBound :: VersionRange -> Bool
230+
hasLEUpperBound = queryVersionRange (\case LEUpperBound -> True; _ -> False) hasLEUpperBound
231+
232+
-- | Is the lower bound version range greater than (GT, >)?
233+
--
234+
-- >>> forM ["< 1", ">= 0 && < 1", ">= 0 || < 1", "> 0 && < 1", "> 0 || < 1", "^>= 4.20.0.0"] (fmap hasGTLowerBound . simpleParsec)
235+
-- Just [False,False,False,True,True,False]
236+
hasGTLowerBound :: VersionRange -> Bool
237+
hasGTLowerBound = queryVersionRange (\case GTLowerBound -> True; _ -> False) hasGTLowerBound
238+
239+
-- | Does the upper bound version range have a trailing zero?
240+
--
241+
-- >>> forM ["< 1", "< 1.1", "< 1.0", "< 1.1.0", "^>= 4.20.0.0"] (fmap hasTrailingZeroUpperBound . simpleParsec)
242+
-- Just [False,False,True,True,False]
243+
hasTrailingZeroUpperBound :: VersionRange -> Bool
244+
hasTrailingZeroUpperBound = queryVersionRange (\case TZUpperBound -> True; _ -> False) hasTrailingZeroUpperBound
245+
246+
queryVersionRange :: (VersionRangeF VersionRange -> Bool) -> (VersionRange -> Bool) -> VersionRange -> Bool
247+
queryVersionRange pf p (projectVersionRange -> v) =
248+
let f = queryVersionRange pf p
249+
in pf v || case v of
250+
IntersectVersionRangesF x y -> f x || f y
251+
UnionVersionRangesF x y -> f x || f y
252+
_ -> False
253+
254+
-- $setup
255+
-- >>> import Distribution.Parsec
256+
-- >>> import Data.Traversable
257+
258+
-- $predicate-examples
259+
--
260+
-- The parsed 'VersionRange' of each version constraint used in the examples for
261+
-- 'hasUpperBound' and 'hasLowerBound' are:
262+
--
263+
-- >>> simpleParsec "< 1" :: Maybe VersionRange
264+
-- Just (EarlierVersion (mkVersion [1]))
265+
-- >>> simpleParsec ">= 0 && < 1" :: Maybe VersionRange
266+
-- Just (IntersectVersionRanges (OrLaterVersion (mkVersion [0])) (EarlierVersion (mkVersion [1])))
267+
-- >>> simpleParsec ">= 0 || < 1" :: Maybe VersionRange
268+
-- Just (UnionVersionRanges (OrLaterVersion (mkVersion [0])) (EarlierVersion (mkVersion [1])))
269+
-- >>> simpleParsec "^>= 4.20.0.0" :: Maybe VersionRange
270+
-- Just (MajorBoundVersion (mkVersion [4,20,0,0]))

Cabal-syntax/src/Distribution/Types/VersionRange/Internal.hs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
{-# LANGUAGE DeriveTraversable #-}
44
{-# LANGUAGE FlexibleContexts #-}
55
{-# LANGUAGE OverloadedStrings #-}
6+
{-# LANGUAGE PatternSynonyms #-}
67
{-# LANGUAGE ScopedTypeVariables #-}
8+
{-# LANGUAGE ViewPatterns #-}
79

810
-- | The only purpose of this module is to prevent the export of
911
-- 'VersionRange' constructors from
@@ -23,7 +25,7 @@ module Distribution.Types.VersionRange.Internal
2325
, intersectVersionRanges
2426
, withinVersion
2527
, majorBoundVersion
26-
, VersionRangeF (..)
28+
, VersionRangeF (.., LEUpperBound, GTLowerBound, TZUpperBound)
2729
, projectVersionRange
2830
, embedVersionRange
2931
, cataVersionRange
@@ -184,6 +186,22 @@ data VersionRangeF a
184186
, Traversable
185187
)
186188

189+
pattern LEUpperBound, GTLowerBound, TZUpperBound :: VersionRangeF a
190+
pattern LEUpperBound <- OrEarlierVersionF _
191+
pattern GTLowerBound <- LaterVersionF _
192+
pattern TZUpperBound <- (upperTrailingZero -> True)
193+
194+
upperTrailingZero :: VersionRangeF a -> Bool
195+
upperTrailingZero (OrEarlierVersionF x) = trailingZero x
196+
upperTrailingZero (EarlierVersionF x) = trailingZero x
197+
upperTrailingZero _ = False
198+
199+
trailingZero :: Version -> Bool
200+
trailingZero (versionNumbers -> vs)
201+
| [0] <- vs = False
202+
| 0 : _ <- reverse vs = True
203+
| otherwise = False
204+
187205
-- | Generic destructor for 'VersionRange'.
188206
--
189207
-- @since 2.2

Cabal-syntax/src/Distribution/Version.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ module Distribution.Version
5050
, stripParensVersionRange
5151
, hasUpperBound
5252
, hasLowerBound
53+
, hasLEUpperBound
54+
, hasTrailingZeroUpperBound
55+
, hasGTLowerBound
5356

5457
-- ** Cata & ana
5558
, VersionRangeF (..)

Cabal-tests/tests/ParserTests/regressions/issue-8646.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ license: BSD-3-Clause
1111

1212
executable test
1313
main-is: ExeMain.hs
14-
build-depends: base > 4 && < 5
14+
build-depends: base >= 4 && < 5
1515
default-language: Haskell2010
1616
ghc-options: -main-is ExeMain

Cabal/src/Distribution/PackageDescription/Check.hs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,20 @@ checkSetupBuildInfo (Just (SetupBuildInfo ds _)) = do
568568
rck =
569569
PackageDistSuspiciousWarn
570570
. MissingUpperBounds CETSetup
571-
checkPVP ick is
572-
checkPVPs rck rs
571+
leuck =
572+
PackageDistSuspiciousWarn
573+
. LEUpperBounds CETSetup
574+
tzuck =
575+
PackageDistSuspiciousWarn
576+
. TrailingZeroUpperBounds CETSetup
577+
gtlck =
578+
PackageDistSuspiciousWarn
579+
. GTLowerBounds CETSetup
580+
checkPVP (checkDependencyVersionRange $ not . hasUpperBound) ick is
581+
checkPVPs (checkDependencyVersionRange $ not . hasUpperBound) rck rs
582+
checkPVPs (checkDependencyVersionRange hasLEUpperBound) leuck ds
583+
checkPVPs (checkDependencyVersionRange hasTrailingZeroUpperBound) tzuck ds
584+
checkPVPs (checkDependencyVersionRange hasGTLowerBound) gtlck ds
573585

574586
checkPackageId :: Monad m => PackageIdentifier -> CheckM m ()
575587
checkPackageId (PackageIdentifier pkgName_ _pkgVersion_) = do

Cabal/src/Distribution/PackageDescription/Check/Common.hs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module Distribution.PackageDescription.Check.Common
1616
, partitionDeps
1717
, checkPVP
1818
, checkPVPs
19+
, checkDependencyVersionRange
1920
) where
2021

2122
import Distribution.Compat.Prelude
@@ -116,34 +117,32 @@ partitionDeps ads ns ds = do
116117
-- for important dependencies like base).
117118
checkPVP
118119
:: Monad m
119-
=> (String -> PackageCheck) -- Warn message depends on name
120+
=> (Dependency -> Bool)
121+
-> (String -> PackageCheck) -- Warn message depends on name
120122
-- (e.g. "base", "Cabal").
121123
-> [Dependency]
122124
-> CheckM m ()
123-
checkPVP ckf ds = do
124-
let ods = checkPVPPrim ds
125+
checkPVP p ckf ds = do
126+
let ods = filter p ds
125127
mapM_ (tellP . ckf . unPackageName . depPkgName) ods
126128

127129
-- PVP dependency check for a list of dependencies. Some code duplication
128130
-- is sadly needed to provide more ergonimic error messages.
129131
checkPVPs
130132
:: Monad m
131-
=> ( [String]
133+
=> (Dependency -> Bool)
134+
-> ( [String]
132135
-> PackageCheck -- Grouped error message, depends on a
133136
-- set of names.
134137
)
135138
-> [Dependency] -- Deps to analyse.
136139
-> CheckM m ()
137-
checkPVPs cf ds
140+
checkPVPs p cf ds
138141
| null ns = return ()
139142
| otherwise = tellP (cf ns)
140143
where
141-
ods = checkPVPPrim ds
144+
ods = filter p ds
142145
ns = map (unPackageName . depPkgName) ods
143146

144-
-- Returns dependencies without upper bounds.
145-
checkPVPPrim :: [Dependency] -> [Dependency]
146-
checkPVPPrim ds = filter withoutUpper ds
147-
where
148-
withoutUpper :: Dependency -> Bool
149-
withoutUpper (Dependency _ ver _) = not . hasUpperBound $ ver
147+
checkDependencyVersionRange :: (VersionRange -> Bool) -> Dependency -> Bool
148+
checkDependencyVersionRange p (Dependency _ ver _) = p ver

Cabal/src/Distribution/PackageDescription/Check/Target.hs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,17 +331,30 @@ checkBuildInfo cet ams ads bi = do
331331
checkAutogenModules ams bi
332332

333333
-- PVP: we check for base and all other deps.
334+
let ds = mergeDependencies $ targetBuildDepends bi
334335
(ids, rds) <-
335336
partitionDeps
336337
ads
337338
[mkUnqualComponentName "base"]
338-
(mergeDependencies $ targetBuildDepends bi)
339+
ds
339340
let ick = const (PackageDistInexcusable BaseNoUpperBounds)
340341
rck = PackageDistSuspiciousWarn . MissingUpperBounds cet
341-
checkPVP ick ids
342+
leuck = PackageDistSuspiciousWarn . LEUpperBounds cet
343+
tzuck = PackageDistSuspiciousWarn . TrailingZeroUpperBounds cet
344+
gtlck = PackageDistSuspiciousWarn . GTLowerBounds cet
345+
checkPVP (checkDependencyVersionRange $ not . hasUpperBound) ick ids
342346
unless
343347
(isInternalTarget cet)
344-
(checkPVPs rck rds)
348+
(checkPVPs (checkDependencyVersionRange $ not . hasUpperBound) rck rds)
349+
unless
350+
(isInternalTarget cet)
351+
(checkPVPs (checkDependencyVersionRange hasLEUpperBound) leuck ds)
352+
unless
353+
(isInternalTarget cet)
354+
(checkPVPs (checkDependencyVersionRange hasTrailingZeroUpperBound) tzuck ds)
355+
unless
356+
(isInternalTarget cet)
357+
(checkPVPs (checkDependencyVersionRange hasGTLowerBound) gtlck ds)
345358

346359
-- Custom fields well-formedness (ASCII).
347360
mapM_ checkCustomField (customFieldsBI bi)

0 commit comments

Comments
 (0)