mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
Warn on potentially un-upgradable language extensions (#7662)
* Warn on potentially un-upgradable language extensions We issue a warning whenever a module is compiled with a language extension enabled for which we are not certain that it works with data-dependencies. Currently, we consider all extensions except for the ones enables by default, `ApplicativeDo` and `PartialTypeSignatures` problematic in this regard. The list of extensions that work fine with data-dependencies might extend over time but we need to do further research and add further tests before we add any extensions. The warning is always put at the location of the module name regardless of where the `{-# LANGUAGE ... #-}` pragma is actually used. This is due to the fact that the `ParsedModule` we get from GHC does not contain the location information of these pragmas. We should probably improve this over time but I think it is acceptable for now. CHANGELOG_BEGIN [DAML Compiler] Warn when a module uses a language extension that might not work with data-dependencies. CHANGELOG_END * Make the warning less polite :) Co-authored-by: Moritz Kiefer <moritz.kiefer@purelyfunctional.org> Co-authored-by: Moritz Kiefer <moritz.kiefer@purelyfunctional.org>
This commit is contained in:
parent
ac4fef2d44
commit
fb33decf09
@ -20,6 +20,7 @@ module DA.Daml.Options
|
||||
, setupDamlGHC
|
||||
, toCompileOpts
|
||||
, PackageDynFlags(..)
|
||||
, dataDependableExtensions
|
||||
) where
|
||||
|
||||
import Control.Exception
|
||||
@ -31,6 +32,7 @@ import Data.IORef
|
||||
import Data.List.Extra
|
||||
import Data.Maybe (fromMaybe)
|
||||
import DynFlags (parseDynamicFilePragma)
|
||||
import qualified EnumSet as ES
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.Text as T
|
||||
import Config (cProjectVersion)
|
||||
@ -64,7 +66,7 @@ import SdkVersion (damlStdlib)
|
||||
toCompileOpts :: Options -> Ghcide.IdeReportProgress -> Ghcide.IdeOptions
|
||||
toCompileOpts options@Options{..} reportProgress =
|
||||
Ghcide.IdeOptions
|
||||
{ optPreprocessor = if optIsGenerated then generatedPreprocessor else damlPreprocessor (optUnitId options)
|
||||
{ optPreprocessor = if optIsGenerated then generatedPreprocessor else damlPreprocessor dataDependableExtensions (optUnitId options)
|
||||
, optGhcSession = getDamlGhcSession options
|
||||
, optPkgLocationOpts = Ghcide.IdePkgLocationOptions
|
||||
{ optLocateHieFile = locateInPkgDb "hie"
|
||||
@ -239,7 +241,12 @@ runGhcFast act = do
|
||||
-- | Language options enabled in the DAML-1.2 compilation
|
||||
xExtensionsSet :: [Extension]
|
||||
xExtensionsSet =
|
||||
[ -- syntactic convenience
|
||||
[ -- Haskell 2010 extensions which are enabled by default (we would need to
|
||||
-- list them for `dataDependableExtensions` below anyway, so let's make
|
||||
-- them explicit here)
|
||||
ImplicitPrelude, StarIsType, MonomorphismRestriction, TraditionalRecordSyntax
|
||||
, EmptyDataDecls, PatternGuards, DoAndIfThenElse, RelaxedPolyRec, NondecreasingIndentation
|
||||
, -- syntactic convenience
|
||||
RecordPuns, RecordWildCards, LambdaCase, TupleSections, BlockArguments, ViewPatterns,
|
||||
NumericUnderscores
|
||||
-- records
|
||||
@ -265,10 +272,22 @@ xExtensionsSet =
|
||||
, DamlSyntax
|
||||
]
|
||||
|
||||
-- | Extensions which we support with data-dependencies.
|
||||
dataDependableExtensions :: ES.EnumSet Extension
|
||||
dataDependableExtensions = ES.fromList $ xExtensionsSet ++
|
||||
[ -- useful for beginners to learn about type inference
|
||||
PartialTypeSignatures
|
||||
-- needed for script and triggers
|
||||
, ApplicativeDo
|
||||
]
|
||||
|
||||
-- | Language settings _disabled_ ($-XNo...$) in the DAML-1.2 compilation
|
||||
xExtensionsUnset :: [Extension]
|
||||
xExtensionsUnset = [ ]
|
||||
xExtensionsUnset =
|
||||
[ -- This is part of Haskell 2010 and would hence be enabled by default,
|
||||
-- which makes zero sense for DAML.
|
||||
ForeignFunctionInterface
|
||||
]
|
||||
|
||||
-- | Flags set for DAML-1.2 compilation
|
||||
xFlagsSet :: Options -> [GeneralFlag]
|
||||
|
@ -14,11 +14,13 @@ import DA.Daml.Preprocessor.EnumType
|
||||
|
||||
import Development.IDE.Types.Options
|
||||
import qualified "ghc-lib" GHC
|
||||
import qualified "ghc-lib-parser" EnumSet as ES
|
||||
import qualified "ghc-lib-parser" SrcLoc as GHC
|
||||
import qualified "ghc-lib-parser" Module as GHC
|
||||
import qualified "ghc-lib-parser" RdrName as GHC
|
||||
import qualified "ghc-lib-parser" OccName as GHC
|
||||
import qualified "ghc-lib-parser" FastString as GHC
|
||||
import qualified "ghc-lib-parser" GHC.LanguageExtensions.Type as GHC
|
||||
import Outputable
|
||||
|
||||
import Control.Monad.Extra
|
||||
@ -59,11 +61,11 @@ mayImportInternal =
|
||||
]
|
||||
|
||||
-- | Apply all necessary preprocessors
|
||||
damlPreprocessor :: Maybe GHC.UnitId -> GHC.DynFlags -> GHC.ParsedSource -> IdePreprocessedSource
|
||||
damlPreprocessor mbUnitId dflags x
|
||||
damlPreprocessor :: ES.EnumSet GHC.Extension -> Maybe GHC.UnitId -> GHC.DynFlags -> GHC.ParsedSource -> IdePreprocessedSource
|
||||
damlPreprocessor dataDependableExtensions mbUnitId dflags x
|
||||
| maybe False (isInternal ||^ (`elem` mayImportInternal)) name = noPreprocessor dflags x
|
||||
| otherwise = IdePreprocessedSource
|
||||
{ preprocWarnings = checkDamlHeader x ++ checkVariantUnitConstructors x
|
||||
{ preprocWarnings = checkDamlHeader x ++ checkVariantUnitConstructors x ++ checkLanguageExtensions dataDependableExtensions dflags x
|
||||
, preprocErrors = checkImports x ++ checkDataTypes x ++ checkModuleDefinition x ++ checkRecordConstructor x ++ checkModuleName x
|
||||
, preprocSource = recordDotPreprocessor $ importDamlPreprocessor $ genericsPreprocessor mbUnitId $ enumTypePreprocessor "GHC.Types" x
|
||||
}
|
||||
@ -244,6 +246,26 @@ checkModuleDefinition x
|
||||
]
|
||||
| otherwise = []
|
||||
|
||||
checkLanguageExtensions :: ES.EnumSet GHC.Extension -> GHC.DynFlags -> GHC.ParsedSource -> [(GHC.SrcSpan, String)]
|
||||
checkLanguageExtensions dataDependableExtensions dflags x =
|
||||
let exts = ES.toList (GHC.extensionFlags dflags)
|
||||
badExts = filter (\ext -> not (ext `ES.member` dataDependableExtensions)) exts
|
||||
in
|
||||
[ (modNameLoc, warning ext) | ext <- badExts ]
|
||||
where
|
||||
warning ext = unlines
|
||||
[ "Modules compiled with the " ++ show ext ++ " language extension"
|
||||
, "might not work properly with data-dependencies. This might stop the"
|
||||
, "whole package from being extensible or upgradable using other versions"
|
||||
, "of the SDK. Use this language extension at your own risk."
|
||||
]
|
||||
-- NOTE(MH): Neither the `DynFlags` nor the `ParsedSource` contain
|
||||
-- information about where a `{-# LANGUAGE ... #-}` pragma has been used.
|
||||
-- In fact, there might not even be such a pragma if a `-X...` flag has been
|
||||
-- used on the command line. Thus, we always put the warning at the location
|
||||
-- of the module name.
|
||||
modNameLoc = maybe GHC.noSrcSpan GHC.getLoc (GHC.hsmodName (GHC.unLoc x))
|
||||
|
||||
-- Extract all data constructors with their locations
|
||||
universeConDecl :: GHC.ParsedSource -> [GHC.LConDecl GHC.GhcPs]
|
||||
-- equivalent to universeBi, but specialised to be faster
|
||||
|
@ -0,0 +1,8 @@
|
||||
-- Test that we get a warning when using a problematic extension.
|
||||
{-# OPTIONS_GHC -XPatternSynonyms #-}
|
||||
-- @WARN Modules compiled with the PatternSynonyms language extension might not work properly with data-dependencies.
|
||||
-- @INFO Use LANGUAGE pragmas
|
||||
|
||||
module BadExtensionOption where
|
||||
|
||||
pattern Nil = []
|
@ -0,0 +1,7 @@
|
||||
-- Test that we get a warning when using a problematic extension.
|
||||
{-# LANGUAGE PatternSynonyms #-}
|
||||
-- @WARN Modules compiled with the PatternSynonyms language extension might not work properly with data-dependencies.
|
||||
|
||||
module BadExtensionPragma where
|
||||
|
||||
pattern Nil = []
|
@ -1,4 +1,5 @@
|
||||
{-# LANGUAGE ConstrainedClassMethods #-}
|
||||
-- @WARN Modules compiled with the ConstrainedClassMethods language extension might not work properly with data-dependencies.
|
||||
|
||||
-- | This module tests the case where a class method contains a constraint
|
||||
-- not present in the class itself.
|
||||
|
@ -2,7 +2,7 @@
|
||||
-- All rights reserved.
|
||||
|
||||
{-# LANGUAGE ExistentialQuantification #-}
|
||||
|
||||
-- @WARN Modules compiled with the ExistentialQuantification language extension might not work properly with data-dependencies.
|
||||
|
||||
module Constraint where
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
{-# LANGUAGE ExistentialQuantification #-}
|
||||
|
||||
-- @WARN Modules compiled with the ExistentialQuantification language extension might not work properly with data-dependencies.
|
||||
-- @ ERROR range=15:0-15:6; Pattern match with existential type.
|
||||
-- @ TODO Existential quantification
|
||||
|
||||
|
||||
module Existential where
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
{-# LANGUAGE ExistentialQuantification #-}
|
||||
|
||||
-- @WARN Modules compiled with the ExistentialQuantification language extension might not work properly with data-dependencies.
|
||||
-- @ ERROR range=17:0-17:6; Pattern match with existential type.
|
||||
-- @ TODO Existential quantification
|
||||
|
||||
|
||||
module ExistentialSum where
|
||||
|
@ -2,7 +2,7 @@
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
{-# LANGUAGE EmptyCase #-}
|
||||
|
||||
-- @WARN Modules compiled with the EmptyCase language extension might not work properly with data-dependencies.
|
||||
|
||||
module Generics where
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
-- Test that we don't get a warning when using an unproblematic extension.
|
||||
{-# OPTIONS_GHC -XPartialTypeSignatures #-}
|
||||
-- @INFO Use LANGUAGE pragmas
|
||||
|
||||
module GoodExtensionOption where
|
@ -0,0 +1,4 @@
|
||||
-- Test that we don't get a warning when using an unproblematic extension.
|
||||
{-# LANGUAGE PartialTypeSignatures #-}
|
||||
|
||||
module GoodExtensionPragma where
|
@ -0,0 +1,4 @@
|
||||
-- Test that we don't get a warning when using a default extension.
|
||||
{-# LANGUAGE ImplicitPrelude #-}
|
||||
|
||||
module GoodExtensionUseless where
|
@ -2,6 +2,7 @@
|
||||
-- All rights reserved.
|
||||
|
||||
{-# LANGUAGE PatternSynonyms #-}
|
||||
-- @WARN Modules compiled with the PatternSynonyms language extension might not work properly with data-dependencies.
|
||||
|
||||
module PatternSynonyms where
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
-- All rights reserved.
|
||||
|
||||
-- @ ERROR B's Company is run by B and they are 3 years old
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
|
||||
module Records where
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
-- All rights reserved.
|
||||
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
|
||||
-- @ERROR range=11:0-11:15; Data definition, of type type family.
|
||||
|
||||
-- @WARN Modules compiled with the TypeFamilies language extension might not work properly with data-dependencies.
|
||||
-- @WARN Modules compiled with the ExplicitNamespaces language extension might not work properly with data-dependencies.
|
||||
|
||||
module TypeFamily where
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user