Skip to content

Commit e67f193

Browse files
committed
Disallow wildcards and operators when spec is old
Do this already in the Parsec instance parser. This allows removing checks from `D.PD.Check`. Later this would also allow removing non-relevant constructors from `VersionRange`, allowing easier testing. This change causes 10% slowdown: from ``` 135768 files processed 7350 files contained warnings 0 files failed to parse 120.901201s elapsed 117.640431s elapsed 119.663620s elapsed 119.454329s elapsed 119.785214s elapsed ``` to ``` 135768 files processed 31912 files contained warnings 0 files failed to parse 130.969593s elapsed 132.016403s elapsed 134.214536s elapsed 128.753382s elapsed 131.503804s elapsed ``` The slowdown is acceptable, and I have an idea which may mitigate this. Note how warnings grew by a factor. There are plenty of (old) files on Hackage, which don't use correct cabal-version. For that reason we only issue warnings, and not fail. Quirks approach won't scale for these.
1 parent fe146e2 commit e67f193

File tree

5 files changed

+99
-137
lines changed

5 files changed

+99
-137
lines changed

Cabal/Distribution/PackageDescription/Check.hs

Lines changed: 0 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,40 +1253,6 @@ checkCabalVersion pkg =
12531253
++ "the 'other-extensions' field lists extensions that are used in "
12541254
++ "some modules, e.g. via the {-# LANGUAGE #-} pragma."
12551255

1256-
-- check use of "foo (>= 1.0 && < 1.4) || >=1.8 " version-range syntax
1257-
, checkVersion [1,8] (not (null versionRangeExpressions)) $
1258-
PackageDistInexcusable $
1259-
"The package uses full version-range expressions "
1260-
++ "in a 'build-depends' field: "
1261-
++ commaSep (map displayRawDependency versionRangeExpressions)
1262-
++ ". To use this new syntax the package needs to specify at least "
1263-
++ "'cabal-version: >= 1.8'. Alternatively, if broader compatibility "
1264-
++ "is important, then convert to conjunctive normal form, and use "
1265-
++ "multiple 'build-depends:' lines, one conjunct per line."
1266-
1267-
-- check use of "build-depends: foo == 1.*" syntax
1268-
, checkVersion [1,6] (not (null depsUsingWildcardSyntax)) $
1269-
PackageDistInexcusable $
1270-
"The package uses wildcard syntax in the 'build-depends' field: "
1271-
++ commaSep (map prettyShow depsUsingWildcardSyntax)
1272-
++ ". To use this new syntax the package need to specify at least "
1273-
++ "'cabal-version: >= 1.6'. Alternatively, if broader compatibility "
1274-
++ "is important then use: " ++ commaSep
1275-
[ prettyShow (Dependency name (eliminateWildcardSyntax versionRange) Set.empty)
1276-
| Dependency name versionRange _ <- depsUsingWildcardSyntax ]
1277-
1278-
-- check use of "build-depends: foo ^>= 1.2.3" syntax
1279-
, checkVersion [2,0] (not (null depsUsingMajorBoundSyntax)) $
1280-
PackageDistInexcusable $
1281-
"The package uses major bounded version syntax in the "
1282-
++ "'build-depends' field: "
1283-
++ commaSep (map prettyShow depsUsingMajorBoundSyntax)
1284-
++ ". To use this new syntax the package need to specify at least "
1285-
++ "'cabal-version: 2.0'. Alternatively, if broader compatibility "
1286-
++ "is important then use: " ++ commaSep
1287-
[ prettyShow (Dependency name (eliminateMajorBoundSyntax versionRange) Set.empty)
1288-
| Dependency name versionRange _ <- depsUsingMajorBoundSyntax ]
1289-
12901256
, checkVersion [3,0] (any (not . null)
12911257
(concatMap buildInfoField
12921258
[ asmSources
@@ -1312,26 +1278,6 @@ checkCabalVersion pkg =
13121278
"The use of 'virtual-modules' requires the package "
13131279
++ " to specify at least 'cabal-version: >= 2.1'."
13141280

1315-
-- check use of "tested-with: GHC (>= 1.0 && < 1.4) || >=1.8 " syntax
1316-
, checkVersion [1,8] (not (null testedWithVersionRangeExpressions)) $
1317-
PackageDistInexcusable $
1318-
"The package uses full version-range expressions "
1319-
++ "in a 'tested-with' field: "
1320-
++ commaSep (map displayRawDependency testedWithVersionRangeExpressions)
1321-
++ ". To use this new syntax the package needs to specify at least "
1322-
++ "'cabal-version: >= 1.8'."
1323-
1324-
-- check use of "tested-with: GHC == 6.12.*" syntax
1325-
, checkVersion [1,6] (not (null testedWithUsingWildcardSyntax)) $
1326-
PackageDistInexcusable $
1327-
"The package uses wildcard syntax in the 'tested-with' field: "
1328-
++ commaSep (map prettyShow testedWithUsingWildcardSyntax)
1329-
++ ". To use this new syntax the package need to specify at least "
1330-
++ "'cabal-version: >= 1.6'. Alternatively, if broader compatibility "
1331-
++ "is important then use: " ++ commaSep
1332-
[ prettyShow (Dependency name (eliminateWildcardSyntax versionRange) Set.empty)
1333-
| Dependency name versionRange _ <- testedWithUsingWildcardSyntax ]
1334-
13351281
-- check use of "source-repository" section
13361282
, checkVersion [1,6] (not (null (sourceRepos pkg))) $
13371283
PackageDistInexcusable $
@@ -1403,15 +1349,6 @@ checkCabalVersion pkg =
14031349

14041350
buildInfoField field = map field (allBuildInfo pkg)
14051351

1406-
versionRangeExpressions =
1407-
[ dep | dep@(Dependency _ vr _) <- allBuildDepends pkg
1408-
, usesNewVersionRangeSyntax vr ]
1409-
1410-
testedWithVersionRangeExpressions =
1411-
[ Dependency (mkPackageName (prettyShow compiler)) vr Set.empty
1412-
| (compiler, vr) <- testedWith pkg
1413-
, usesNewVersionRangeSyntax vr ]
1414-
14151352
simpleSpecVersionRangeSyntax =
14161353
either (const True) (cataVersionRange alg) (specVersionRaw pkg)
14171354
where
@@ -1422,63 +1359,8 @@ checkCabalVersion pkg =
14221359
simpleSpecVersionSyntax =
14231360
either (const True) (const False) (specVersionRaw pkg)
14241361

1425-
usesNewVersionRangeSyntax :: VersionRange -> Bool
1426-
usesNewVersionRangeSyntax
1427-
= (> 2) -- uses the new syntax if depth is more than 2
1428-
. cataVersionRange alg
1429-
where
1430-
alg (UnionVersionRangesF a b) = a + b
1431-
alg (IntersectVersionRangesF a b) = a + b
1432-
alg (VersionRangeParensF _) = 3
1433-
alg _ = 1 :: Int
1434-
1435-
depsUsingWildcardSyntax = [ dep | dep@(Dependency _ vr _) <- allBuildDepends pkg
1436-
, usesWildcardSyntax vr ]
1437-
1438-
depsUsingMajorBoundSyntax = [ dep | dep@(Dependency _ vr _) <- allBuildDepends pkg
1439-
, usesMajorBoundSyntax vr ]
1440-
14411362
usesBackpackIncludes = any (not . null . mixins) (allBuildInfo pkg)
14421363

1443-
testedWithUsingWildcardSyntax =
1444-
[ Dependency (mkPackageName (prettyShow compiler)) vr Set.empty
1445-
| (compiler, vr) <- testedWith pkg
1446-
, usesWildcardSyntax vr ]
1447-
1448-
usesWildcardSyntax :: VersionRange -> Bool
1449-
usesWildcardSyntax = cataVersionRange alg
1450-
where
1451-
alg (WildcardVersionF _) = True
1452-
alg (UnionVersionRangesF a b) = a || b
1453-
alg (IntersectVersionRangesF a b) = a || b
1454-
alg (VersionRangeParensF a) = a
1455-
alg _ = False
1456-
1457-
-- NB: this eliminates both, WildcardVersion and MajorBoundVersion
1458-
-- because when WildcardVersion is not support, neither is MajorBoundVersion
1459-
eliminateWildcardSyntax = hyloVersionRange embed projectVersionRange
1460-
where
1461-
embed (WildcardVersionF v) = intersectVersionRanges
1462-
(orLaterVersion v) (earlierVersion (wildcardUpperBound v))
1463-
embed (MajorBoundVersionF v) = intersectVersionRanges
1464-
(orLaterVersion v) (earlierVersion (majorUpperBound v))
1465-
embed vr = embedVersionRange vr
1466-
1467-
usesMajorBoundSyntax :: VersionRange -> Bool
1468-
usesMajorBoundSyntax = cataVersionRange alg
1469-
where
1470-
alg (MajorBoundVersionF _) = True
1471-
alg (UnionVersionRangesF a b) = a || b
1472-
alg (IntersectVersionRangesF a b) = a || b
1473-
alg (VersionRangeParensF a) = a
1474-
alg _ = False
1475-
1476-
eliminateMajorBoundSyntax = hyloVersionRange embed projectVersionRange
1477-
where
1478-
embed (MajorBoundVersionF v) = intersectVersionRanges
1479-
(orLaterVersion v) (earlierVersion (majorUpperBound v))
1480-
embed vr = embedVersionRange vr
1481-
14821364
mentionedExtensions = [ ext | bi <- allBuildInfo pkg
14831365
, ext <- allExtensions bi ]
14841366
mentionedExtensionsThatNeedCabal12 =
@@ -1529,11 +1411,6 @@ checkCabalVersion pkg =
15291411

15301412
allModuleNamesAutogen = concatMap autogenModules (allBuildInfo pkg)
15311413

1532-
displayRawDependency :: Dependency -> String
1533-
displayRawDependency (Dependency pkg vr _sublibs) =
1534-
prettyShow pkg ++ " " ++ prettyShow vr
1535-
1536-
15371414
-- ------------------------------------------------------------
15381415
-- * Checks on the GenericPackageDescription
15391416
-- ------------------------------------------------------------

Cabal/Distribution/Parsec.hs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ module Distribution.Parsec (
99
runParsecParser,
1010
runParsecParser',
1111
simpleParsec,
12+
simpleParsec',
13+
simpleParsecW',
1214
lexemeParsec,
1315
eitherParsec,
1416
explicitEitherParsec,
17+
explicitEitherParsec',
1518
-- * CabalParsing and and diagnostics
1619
CabalParsing (..),
1720
-- ** Warnings
@@ -171,6 +174,25 @@ simpleParsec
171174
. runParsecParser lexemeParsec "<simpleParsec>"
172175
. fieldLineStreamFromString
173176

177+
-- | Parse a 'String' with 'lexemeParsec' using specific 'CabalSpecVersion'.
178+
--
179+
-- @since 3.4.0.0
180+
simpleParsec' :: Parsec a => CabalSpecVersion -> String -> Maybe a
181+
simpleParsec' spec
182+
= either (const Nothing) Just
183+
. runParsecParser' spec lexemeParsec "<simpleParsec>"
184+
. fieldLineStreamFromString
185+
186+
-- | Parse a 'String' with 'lexemeParsec' using specific 'CabalSpecVersion'.
187+
-- Fail if there are any warnings.
188+
--
189+
-- @since 3.4.0.0
190+
simpleParsecW' :: Parsec a => CabalSpecVersion -> String -> Maybe a
191+
simpleParsecW' spec
192+
= either (const Nothing) (\(x, ws) -> if null ws then Just x else Nothing)
193+
. runParsecParser' spec ((,) <$> lexemeParsec <*> liftParsec Parsec.getState) "<simpleParsec>"
194+
. fieldLineStreamFromString
195+
174196
-- | Parse a 'String' with 'lexemeParsec'.
175197
eitherParsec :: Parsec a => String -> Either String a
176198
eitherParsec = explicitEitherParsec parsec
@@ -182,6 +204,17 @@ explicitEitherParsec parser
182204
. runParsecParser (parser <* P.spaces) "<eitherParsec>"
183205
. fieldLineStreamFromString
184206

207+
-- | Parse a 'String' with given 'ParsecParser' and 'CabalSpecVersion'. Trailing whitespace is accepted.
208+
-- See 'explicitEitherParsec'.
209+
--
210+
-- @since 3.4.0.0
211+
--
212+
explicitEitherParsec' :: CabalSpecVersion -> ParsecParser a -> String -> Either String a
213+
explicitEitherParsec' spec parser
214+
= either (Left . show) Right
215+
. runParsecParser' spec (parser <* P.spaces) "<eitherParsec>"
216+
. fieldLineStreamFromString
217+
185218
-- | Run 'ParsecParser' with 'cabalSpecLatest'.
186219
runParsecParser :: ParsecParser a -> FilePath -> FieldLineStream -> Either Parsec.ParseError a
187220
runParsecParser = runParsecParser' cabalSpecLatest

Cabal/Distribution/Parsec/Warning.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ data PWarnType
3535
| PWTDoubleDash -- ^ Double dash token, most likely it's a mistake - it's not a comment
3636
| PWTMultipleSingularField -- ^ e.g. name or version should be specified only once.
3737
| PWTBuildTypeDefault -- ^ Workaround for derive-package having build-type: Default. See <https://github.com/haskell/cabal/issues/5020>.
38+
39+
| PWTVersionOperator -- ^ Version operators used (without cabal-version: 1.8)
40+
| PWTVersionWildcard -- ^ Version wildcard used (without cabal-version: 1.6)
3841
deriving (Eq, Ord, Show, Enum, Bounded, Generic)
3942

4043
instance Binary PWarnType

Cabal/Distribution/Types/PkgconfigVersionRange.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ instance Parsec PkgconfigVersionRange where
6969
csv <- askCabalSpecVersion
7070
if csv >= CabalSpecV3_0
7171
then pkgconfigParser
72-
else versionRangeToPkgconfigVersionRange <$> versionRangeParser P.integral
72+
else versionRangeToPkgconfigVersionRange <$> versionRangeParser P.integral csv
7373

7474
-- "modern" parser of @pkg-config@ package versions.
7575
pkgconfigParser :: CabalParsing m => m PkgconfigVersionRange

Cabal/Distribution/Types/VersionRange/Internal.hs

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,40 @@ instance Pretty VersionRange where
262262
punct p p' | p < p' = Disp.parens
263263
| otherwise = id
264264

265+
-- |
266+
--
267+
-- >>> simpleParsec "^>= 3.4" :: Maybe VersionRange
268+
-- Just (MajorBoundVersion (mkVersion [3,4]))
269+
--
270+
-- Small history:
271+
--
272+
-- Set operations are introduced in 3.0
273+
--
274+
-- >>> map (`simpleParsec'` "^>= { 1.2 , 1.3 }") [CabalSpecV2_4, CabalSpecV3_0] :: [Maybe VersionRange]
275+
-- [Nothing,Just (UnionVersionRanges (MajorBoundVersion (mkVersion [1,2])) (MajorBoundVersion (mkVersion [1,3])))]
276+
--
277+
-- @^>=@ is introduced in 2.0
278+
--
279+
-- >>> map (`simpleParsec'` "^>=1.2") [CabalSpecV1_24, CabalSpecV2_0] :: [Maybe VersionRange]
280+
-- [Nothing,Just (MajorBoundVersion (mkVersion [1,2]))]
281+
--
282+
-- @-none@ is introduced in 1.22
283+
--
284+
-- >>> map (`simpleParsec'` "-none") [CabalSpecV1_20, CabalSpecV1_22] :: [Maybe VersionRange]
285+
-- [Nothing,Just (IntersectVersionRanges (LaterVersion (mkVersion [1])) (EarlierVersion (mkVersion [1])))]
286+
--
287+
-- Operators are introduced in 1.8. Issues only a warning.
288+
--
289+
-- >>> map (`simpleParsecW'` "== 1 || ==2") [CabalSpecV1_6, CabalSpecV1_8] :: [Maybe VersionRange]
290+
-- [Nothing,Just (UnionVersionRanges (ThisVersion (mkVersion [1])) (ThisVersion (mkVersion [2])))]
291+
--
292+
-- Wild-version ranges are introduced in 1.6. Issues only a warning.
293+
--
294+
-- >>> map (`simpleParsecW'` "== 1.2.*") [CabalSpecV1_4, CabalSpecV1_6] :: [Maybe VersionRange]
295+
-- [Nothing,Just (WildcardVersion (mkVersion [1,2]))]
296+
--
265297
instance Parsec VersionRange where
266-
parsec = versionRangeParser versionDigitParser
298+
parsec = askCabalSpecVersion >>= versionRangeParser versionDigitParser
267299

268300
instance Described VersionRange where
269301
describe _ = RERec "version-range" $ REUnion
@@ -301,13 +333,14 @@ instance Described VersionRange where
301333
-- versions, 'PkgConfigVersionRange'.
302334
--
303335
-- @since 3.0
304-
versionRangeParser :: forall m. CabalParsing m => m Int -> m VersionRange
305-
versionRangeParser digitParser = expr
336+
versionRangeParser :: forall m. CabalParsing m => m Int -> CabalSpecVersion -> m VersionRange
337+
versionRangeParser digitParser csv = expr
306338
where
307339
expr = do P.spaces
308340
t <- term
309341
P.spaces
310342
(do _ <- P.string "||"
343+
checkOp
311344
P.spaces
312345
e <- expr
313346
return (unionVersionRanges t e)
@@ -316,6 +349,7 @@ versionRangeParser digitParser = expr
316349
term = do f <- factor
317350
P.spaces
318351
(do _ <- P.string "&&"
352+
checkOp
319353
P.spaces
320354
t <- term
321355
return (intersectVersionRanges f t)
@@ -331,6 +365,7 @@ versionRangeParser digitParser = expr
331365
"==" -> do
332366
P.spaces
333367
(do (wild, v) <- verOrWild
368+
checkWild wild
334369
pure $ (if wild then withinVersion else thisVersion) v
335370
<|>
336371
(verSet' thisVersion =<< verSet))
@@ -356,6 +391,27 @@ versionRangeParser digitParser = expr
356391
">" -> pure $ laterVersion v
357392
_ -> fail $ "Unknown version operator " ++ show op
358393

394+
-- Cannot be warning
395+
-- On 2020-03-16 there was around 27400 files on Hackage failing to parse due this
396+
-- For example https://hackage.haskell.org/package/haxr-3000.0.0/haxr.cabal
397+
--
398+
checkOp = when (csv < CabalSpecV1_8) $
399+
parsecWarning PWTVersionOperator $ unwords
400+
[ "version operators used."
401+
, "To use version operators the package needs to specify at least 'cabal-version: >= 1.8'."
402+
]
403+
404+
-- Cannot be warning
405+
-- On 2020-03-16 there was 46 files on Hackage failing to parse due this
406+
-- For example https://hackage.haskell.org/package/derive-0.1.2/derive.cabal
407+
--
408+
checkWild False = pure ()
409+
checkWild True = when (csv < CabalSpecV1_6) $
410+
parsecWarning PWTVersionWildcard $ unwords
411+
[ "Wildcard syntax used."
412+
, "To use version wildcards the package needs to specify at least 'cabal-version: >= 1.6'."
413+
]
414+
359415
-- https://gitlab.haskell.org/ghc/ghc/issues/17752
360416
isOpChar '<' = True
361417
isOpChar '=' = True
@@ -364,13 +420,8 @@ versionRangeParser digitParser = expr
364420
isOpChar '-' = True
365421
isOpChar _ = False
366422

367-
-- Note: There are other features:
368-
-- && and || since 1.8
369-
-- x.y.* (wildcard) since 1.6
370-
371423
-- -none version range is available since 1.22
372-
noVersion' = do
373-
csv <- askCabalSpecVersion
424+
noVersion' =
374425
if csv >= CabalSpecV1_22
375426
then pure noVersion
376427
else fail $ unwords
@@ -381,8 +432,7 @@ versionRangeParser digitParser = expr
381432
]
382433

383434
-- ^>= is available since 2.0
384-
majorBoundVersion' v = do
385-
csv <- askCabalSpecVersion
435+
majorBoundVersion' v =
386436
if csv >= CabalSpecV2_0
387437
then pure $ majorBoundVersion v
388438
else fail $ unwords
@@ -398,8 +448,7 @@ versionRangeParser digitParser = expr
398448
embed vr = embedVersionRange vr
399449

400450
-- version set notation (e.g. "== { 0.0.1.0, 0.0.2.0, 0.1.0.0 }")
401-
verSet' op vs = do
402-
csv <- askCabalSpecVersion
451+
verSet' op vs =
403452
if csv >= CabalSpecV3_0
404453
then pure $ foldr1 unionVersionRanges (fmap op vs)
405454
else fail $ unwords

0 commit comments

Comments
 (0)