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:
Martin Huschenbett 2020-10-15 19:40:13 +02:00 committed by GitHub
parent ac4fef2d44
commit fb33decf09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 83 additions and 13 deletions

View File

@ -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]

View File

@ -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

View File

@ -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 = []

View File

@ -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 = []

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
-- Test that we don't get a warning when using an unproblematic extension.
{-# LANGUAGE PartialTypeSignatures #-}
module GoodExtensionPragma where

View File

@ -0,0 +1,4 @@
-- Test that we don't get a warning when using a default extension.
{-# LANGUAGE ImplicitPrelude #-}
module GoodExtensionUseless where

View File

@ -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

View File

@ -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

View File

@ -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