diff --git a/COPYING.BSD3.base b/COPYING.BSD3.base new file mode 100644 index 000000000..c362f2d90 --- /dev/null +++ b/COPYING.BSD3.base @@ -0,0 +1,83 @@ +This library (libraries/base) is derived from code from several +sources: + + * Code from the GHC project which is largely (c) The University of + Glasgow, and distributable under a BSD-style license (see below), + + * Code from the Haskell 98 Report which is (c) Simon Peyton Jones + and freely redistributable (but see the full license for + restrictions). + + * Code from the Haskell Foreign Function Interface specification, + which is (c) Manuel M. T. Chakravarty and freely redistributable + (but see the full license for restrictions). + +The full text of these licenses is reproduced below. All of the +licenses are BSD-style or compatible. + +----------------------------------------------------------------------------- + +The Glasgow Haskell Compiler License + +Copyright 2004, The University Court of the University of Glasgow. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +- Neither name of the University nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF +GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +----------------------------------------------------------------------------- + +Code derived from the document "Report on the Programming Language +Haskell 98", is distributed under the following license: + + Copyright (c) 2002 Simon Peyton Jones + + The authors intend this Report to belong to the entire Haskell + community, and so we grant permission to copy and distribute it for + any purpose, provided that it is reproduced in its entirety, + including this Notice. Modified versions of this Report may also be + copied and distributed for any purpose, provided that the modified + version is clearly presented as such, and that it does not claim to + be a definition of the Haskell 98 Language. + +----------------------------------------------------------------------------- + +Code derived from the document "The Haskell 98 Foreign Function +Interface, An Addendum to the Haskell 98 Report" is distributed under +the following license: + + Copyright (c) 2002 Manuel M. T. Chakravarty + + The authors intend this Report to belong to the entire Haskell + community, and so we grant permission to copy and distribute it for + any purpose, provided that it is reproduced in its entirety, + including this Notice. Modified versions of this Report may also be + copied and distributed for any purpose, provided that the modified + version is clearly presented as such, and that it does not claim to + be a definition of the Haskell 98 Foreign Function Interface. + +----------------------------------------------------------------------------- diff --git a/COPYING.BSD3.ghc b/COPYING.BSD3.ghc new file mode 100644 index 000000000..92337b951 --- /dev/null +++ b/COPYING.BSD3.ghc @@ -0,0 +1,31 @@ +The Glasgow Haskell Compiler License + +Copyright 2004, The University Court of the University of Glasgow. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +- Neither name of the University nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF +GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/COPYING.BSD3.haddock-api b/COPYING.BSD3.haddock-api new file mode 100644 index 000000000..460decfca --- /dev/null +++ b/COPYING.BSD3.haddock-api @@ -0,0 +1,23 @@ +Copyright 2002-2010, Simon Marlow. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/GhcMod/Exe/ImportedFrom.hs b/GhcMod/Exe/ImportedFrom.hs new file mode 100644 index 000000000..4365b28c3 --- /dev/null +++ b/GhcMod/Exe/ImportedFrom.hs @@ -0,0 +1,242 @@ +-- Copyright (C) 2013-2016 Carlo Hamalainen +-- Copyright (C) 2016 Daniel Gröber +-- Copyright (C) 2016 Nikolay Yakimov +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . + +module GhcMod.Exe.ImportedFrom (importedFrom) where + +import Control.Applicative +import Control.Exception +import Data.List +import Data.Maybe +import System.FilePath + +import Exception (ghandle) +import GHC +import OccName +import Packages +import HscTypes + +import GhcMod +import GhcMod.DynFlags +import GhcMod.FileMapping +import GhcMod.Gap +import GhcMod.Logging +import GhcMod.Monad +import GhcMod.SrcUtils (listifyStaged, findSpanName, cmp) +import GHC.SYB.Utils +import Data.Function +import Data.Version +import Data.Traversable +import Prelude hiding (mapM) +import Data.Data +import Safe +import Documentation.Haddock +import Data.IORef +import System.Directory +import Control.Monad.Trans (lift) +import Control.Monad.Trans.Maybe + +data PackageDesc = PackageDesc + { pdName :: String + , pdVersion :: Version + , pdHdHTMLs :: [FilePath] + , pdHdIfaces :: [InstalledInterface] + } + +data ModuleDesc = ModuleDesc + { mdName :: String + , mdMod :: Module + , mdAlias :: Maybe String + , mdVisibleExports :: [Name] + , mdImplicit :: Bool + } + +getPackageDescFromPackageConfig :: (GmOut m, GmLog m, GhcMonad m, MonadIO m) + => PackageConfig -> m PackageDesc +getPackageDescFromPackageConfig p@InstalledPackageInfo{..} + = do + let (pkgName, pkgVer) = packageNameVesrion p + his <- catMaybes <$> mapM readInterfaceFile' haddockInterfaces + return PackageDesc + { pdName = pkgName + , pdVersion = pkgVer + , pdHdHTMLs = haddockHTMLs + , pdHdIfaces = concatMap ifInstalledIfaces his + } + +readInterfaceFile' :: (GmOut m, GmLog m, MonadIO m, GhcMonad m) + => FilePath -> m (Maybe InterfaceFile) +readInterfaceFile' f = do + exists <- liftIO $ doesFileExist f + if exists + then either (const Nothing) Just <$> readInterfaceFile nameCacheFromGhc' f + else do + gmLog GmWarning "imported-from" haddockSuggestion + return Nothing + where + backticks :: GhcMod.Logging.Doc -> GhcMod.Logging.Doc + backticks d = text "`" $$ d $$ text "`" + + haddockSuggestion = + text "Couldn't find haddock interface" <+> quotes (text f) $$ + text "- To generate Haddock docs for dependencies, try:" $$ + nest 4 (backticks $ text "cabal install --enable-documentation\ + \--haddock-hyperlink-source\ + \--only-dependencies") $$ + text "" $$ + text "- or set" $$ + nest 4 (backticks $ text "documentation: True") $$ + text "in ~/.cabal/config" $$ + text "" $$ + text "- or with Stack:" $$ + nest 4 (backticks $ text "stack haddock") + +-- Derived from haddock-api, see COPYING.BSD3.haddock-api in the source +-- distribution for it's license. +nameCacheFromGhc' :: (GhcMonad m, MonadIO m) => NameCacheAccessor m +nameCacheFromGhc' = ( read_from_session , write_to_session ) + where + read_from_session = liftIO =<< readIORef . hsc_NC <$> getSession + write_to_session nc' = liftIO =<< flip writeIORef nc' . hsc_NC <$> getSession + +getModulePackage :: (GmOut m, GmLog m, GhcMonad m, MonadIO m) + => Module -> m (Maybe PackageDesc) +getModulePackage m = do + dflag <- getSessionDynFlags + let pkg = lookupPackage' dflag (moduleUnitId' m) + mapM getPackageDescFromPackageConfig pkg + +getModuleHaddockVisibleExports :: ModuleDesc -> PackageDesc -> [Name] +getModuleHaddockVisibleExports ModuleDesc{..} pkgdesc = + let modHdIfs = filter ((mdMod ==) . instMod) . pdHdIfaces $ pkgdesc + in concatMap instVisibleExports modHdIfs + +getModuleDescFromImport :: (GhcMonad m) => ImportDecl Name -> m ModuleDesc +getModuleDescFromImport ImportDecl{..} = do + modul <- findModule (unLoc ideclName) (fmap sl_fs' ideclPkgQual) + modInfo <- fromJustNote "getModuleDescFromImport" <$> getModuleInfo modul + let listNames :: Data a => a -> [Name] + listNames = listifyStaged Renamer (const True) + exprts = modInfoExports modInfo + visExprts + = case ideclHiding of + Just (True, hidden) -> exprts \\ listNames hidden + Just (False, shown) -> listNames shown + Nothing -> exprts + return ModuleDesc + { mdName = moduleNameString (moduleName modul) + , mdMod = modul + , mdAlias = moduleNameString <$> ideclAs + , mdVisibleExports = visExprts + , mdImplicit = ideclImplicit + } + +modulesWithPackages :: (GmOut m, GmLog m, GhcMonad m, MonadIO m) + => [ModuleDesc] -> m [(ModuleDesc, PackageDesc)] +modulesWithPackages = + (fmap catMaybes .) $ mapM $ \x@ModuleDesc{..} -> runMaybeT $ do + pkg <- MaybeT $ getModulePackage mdMod + return (x, pkg) + +guessModule :: Maybe String + -> Name + -> [(ModuleDesc, PackageDesc)] + -> Maybe (Name, (ModuleDesc, PackageDesc)) +guessModule mqn n ms = let + occn = occNameString $ occName n + msf = filter f ms + f = (n `elem`) . uncurry getModuleHaddockVisibleExports + msf2 | null msf = filter f2 ms + | otherwise = msf + f2 (ModuleDesc{..},_) = n `elem` mdVisibleExports + msf3 | Just qn <- mqn + , qn /= occn = filter (f3 qn) msf2 + | otherwise = msf2 + f3 qn (ModuleDesc{..},_) + | Just as <- mdAlias = qn `elem` map (++ '.' : occn) [as, mdName] + | otherwise = qn == (mdName ++ '.' : occn) + in + (,) n <$> headMay msf3 + +showOutput :: (GmOut m, GmLog m, GhcMonad m, MonadIO m) + => Name -> (ModuleDesc, PackageDesc) -> m String +showOutput n (ModuleDesc{..}, imppkg) = do + let occn = occNameString $ occName n + nmod = nameModule n + mn = moduleNameString . moduleName $ nmod + modpkg <- fromMaybe imppkg <$> getModulePackage nmod + let modpackage + | null (versionBranch modpackagever) = pdName modpkg + | otherwise = pdName modpkg ++ '-' : showVersion modpackagever + modpackagever = pdVersion modpkg + package + | null (versionBranch packagever) + , Just r <- hdRoot = takeFileName r + | otherwise = pdName imppkg ++ '-' : showVersion packagever + packagever = pdVersion imppkg + fqn = modpackage ++ ':' : mn ++ '.' : occn + hdRoot = headMay $ pdHdHTMLs imppkg + docFn = dotsToDashes mdName ++ ".html" + hdPath = fmap ( docFn) hdRoot + dotsToDashes = map go + where go '.' = '-' + go x = x + hackageUrl = "https://hackage.haskell.org/package/" + ++ package ++ "/docs/" ++ docFn + hdPathReal <- liftIO $ runMaybeT $ do + hdp <- MaybeT $ return hdPath + exists <- lift $ doesFileExist hdp + if exists + then return hdp + else MaybeT $ return Nothing + return $ unwords [fqn, mdName, fromMaybe hackageUrl hdPathReal] + +-- | Look up Haddock docs for a symbol. +importedFrom :: forall m. IOish m + => FilePath -- ^ A target file. + -> Int -- ^ Line number. + -> Int -- ^ Column number. + -> Maybe Expression -- ^ Expression (symbol) + -> GhcModT m String +importedFrom file lineNr colNr symbol = + handler $ runGmlT' [Left file] deferErrors $ withInteractiveContext $ do + crdl <- cradle + modSum <- fileModSummaryWithMapping (cradleCurrentDir crdl file) + Just (decls,imports, _exports, _docs) + <- renamedSource <$> (parseModule modSum >>= typecheckModule) + importDescs :: [ModuleDesc] + <- mapM (getModuleDescFromImport . unLoc) imports + bestids <- + case sortBy (cmp `on` fst) $ findSpanName decls (lineNr, colNr) of + ((_, x):_) -> return x + [] -> error $ "No names found at " ++ show (lineNr, colNr) + let visExports xs n = filter ((elem n) . mdVisibleExports) xs + idsMods = map (preferExplicit . visExports importDescs) bestids + mbsym = getExpression <$> symbol + imps <- mapM modulesWithPackages idsMods + let bestids_str = + intercalate "," (map (occNameString . getOccName) bestids) + bg <- case catMaybes $ zipWith (guessModule mbsym) bestids imps of + [] -> error $ "No modules exporting " ++ fromMaybe bestids_str mbsym + x -> return x + unlines <$> mapM (uncurry showOutput) bg + where + handler = ghandle $ \(SomeException ex) -> + gmLog GmException "imported-from" (showToDoc ex) >> return [] + + preferExplicit :: [ModuleDesc] -> [ModuleDesc] + preferExplicit ms = + let (impl, expl) = partition mdImplicit ms in expl ++ impl diff --git a/LICENSE b/LICENSE index c646aebdf..d93f7d586 100644 --- a/LICENSE +++ b/LICENSE @@ -4,3 +4,7 @@ under this license and can generally be identified by the lack of a GPL header. See the files COPYING.BSD3 and COPYING.AGPL3 in the source distribution for copies of the two licenses. + +We also incorporate some code from the Glasgow Haskell Compiler and related +projects. See the remaining COPYING.* files in the source distribution for their +respective licenses. \ No newline at end of file diff --git a/core/GhcMod/DebugLogger.hs b/core/GhcMod/DebugLogger.hs index 6e9dfa821..c199ae79c 100644 --- a/core/GhcMod/DebugLogger.hs +++ b/core/GhcMod/DebugLogger.hs @@ -13,42 +13,13 @@ -- -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see . + +-- This module is derived from GHC, see COPYING.BSD3.ghc in the source +-- distribution for it's license. + {-# LANGUAGE CPP, RankNTypes #-} module GhcMod.DebugLogger where --- (c) The University of Glasgow 2005 --- --- The Glasgow Haskell Compiler License --- --- Copyright 2002, The University Court of the University of Glasgow. --- All rights reserved. --- --- Redistribution and use in source and binary forms, with or without --- modification, are permitted provided that the following conditions are met: --- --- - Redistributions of source code must retain the above copyright notice, --- this list of conditions and the following disclaimer. --- --- - Redistributions in binary form must reproduce the above copyright notice, --- this list of conditions and the following disclaimer in the documentation --- and/or other materials provided with the distribution. --- --- - Neither name of the University nor the names of its contributors may be --- used to endorse or promote products derived from this software without --- specific prior written permission. --- --- THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF --- GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, --- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND --- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE --- UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE --- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL --- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR --- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER --- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT --- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY --- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH --- DAMAGE. import GHC import FastString diff --git a/core/GhcMod/Gap.hs b/core/GhcMod/Gap.hs index 728a04ca9..b9af22f67 100644 --- a/core/GhcMod/Gap.hs +++ b/core/GhcMod/Gap.hs @@ -49,6 +49,10 @@ module GhcMod.Gap ( , mkErrStyle' , everythingStagedWithContext , withCleanupSession + , moduleUnitId' + , sl_fs' + , packageNameVesrion + , lookupPackage' ) where import Control.Applicative hiding (empty) @@ -120,6 +124,8 @@ import RdrName (rdrNameOcc) #if __GLASGOW_HASKELL__ < 710 import UniqFM (eltsUFM) import Module +import Data.Generics.Schemes (gfindtype) +import Safe #endif #if __GLASGOW_HASKELL__ >= 704 @@ -131,6 +137,10 @@ import Control.DeepSeq (NFData(rnf)) import Data.ByteString.Lazy.Internal (ByteString(..)) #endif +#if __GLASGOW_HASKELL__ >= 800 +import BasicTypes (sl_fs, StringLiteral) +#endif + import Bag import Lexer as L import Parser @@ -721,3 +731,51 @@ withCleanupSession action = do df <- getSessionDynFlags GHC.defaultCleanupHandler df action #endif + +-- | Things for Language.Haskell.GhcMod.ImportedFrom + +#if __GLASGOW_HASKELL__ >= 800 +moduleUnitId' :: Module -> UnitId +moduleUnitId' = GHC.moduleUnitId +#elif __GLASGOW_HASKELL__ >= 710 +moduleUnitId' :: Module -> PackageKey +moduleUnitId' = GHC.modulePackageKey +#else +moduleUnitId' :: Module -> PackageId +moduleUnitId' = GHC.modulePackageId +#endif + +#if __GLASGOW_HASKELL__ >= 800 +sl_fs' :: StringLiteral -> FastString +sl_fs' = sl_fs +#else +sl_fs' :: FastString -> FastString +sl_fs' = id +#endif + + +#if __GLASGOW_HASKELL__ >= 710 +packageNameVesrion :: PackageConfig -> (String, Version) +packageNameVesrion + InstalledPackageInfo{packageName=PackageName pn, packageVersion=pv} + = (unpackFS pn, pv) +#else +packageNameVesrion :: PackageConfig -> (String, Version) +packageNameVesrion + InstalledPackageInfo{sourcePackageId=PackageIdentifier{pkgName=pn, pkgVersion=pv}} + -- here, pkgName is `Distribution.Package.PackageName String` from Cabal + -- using gfindtype to avoid dependence on Cabal-1.18.1.5 + = (fromJustNote "Gap,packageNameVersion" (gfindtype pn), pv) +#endif + +#if __GLASGOW_HASKELL__ >= 710 +#if __GLASGOW_HASKELL__ >= 800 +lookupPackage' :: DynFlags -> UnitId -> Maybe PackageConfig +#else +lookupPackage' :: DynFlags -> PackageKey -> Maybe PackageConfig +#endif +lookupPackage' = lookupPackage +#else +lookupPackage' :: DynFlags -> PackageId -> Maybe PackageConfig +lookupPackage' = lookupPackage . pkgIdMap . pkgState +#endif diff --git a/core/GhcMod/Read.hs b/core/GhcMod/Read.hs index 3297f5122..8c35f1d5a 100644 --- a/core/GhcMod/Read.hs +++ b/core/GhcMod/Read.hs @@ -4,90 +4,6 @@ import Text.Read (readPrec_to_S, readPrec, minPrec) import qualified Text.ParserCombinators.ReadP as P import Text.ParserCombinators.ReadPrec (lift) --- This library (libraries/base) is derived from code from several --- sources: - --- * Code from the GHC project which is largely (c) The University of --- Glasgow, and distributable under a BSD-style license (see below), - --- * Code from the Haskell 98 Report which is (c) Simon Peyton Jones --- and freely redistributable (but see the full license for --- restrictions). - --- * Code from the Haskell Foreign Function Interface specification, --- which is (c) Manuel M. T. Chakravarty and freely redistributable --- (but see the full license for restrictions). - --- The full text of these licenses is reproduced below. All of the --- licenses are BSD-style or compatible. - --- ----------------------------------------------------------------------------- - --- The Glasgow Haskell Compiler License - --- Copyright 2004, The University Court of the University of Glasgow. --- All rights reserved. - --- Redistribution and use in source and binary forms, with or without --- modification, are permitted provided that the following conditions are met: - --- - Redistributions of source code must retain the above copyright notice, --- this list of conditions and the following disclaimer. - --- - Redistributions in binary form must reproduce the above copyright notice, --- this list of conditions and the following disclaimer in the documentation --- and/or other materials provided with the distribution. - --- - Neither name of the University nor the names of its contributors may be --- used to endorse or promote products derived from this software without --- specific prior written permission. - --- THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF --- GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, --- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND --- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE --- UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE --- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL --- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR --- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER --- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT --- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY --- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH --- DAMAGE. - --- ----------------------------------------------------------------------------- - --- Code derived from the document "Report on the Programming Language --- Haskell 98", is distributed under the following license: - --- Copyright (c) 2002 Simon Peyton Jones - --- The authors intend this Report to belong to the entire Haskell --- community, and so we grant permission to copy and distribute it for --- any purpose, provided that it is reproduced in its entirety, --- including this Notice. Modified versions of this Report may also be --- copied and distributed for any purpose, provided that the modified --- version is clearly presented as such, and that it does not claim to --- be a definition of the Haskell 98 Language. - --- ----------------------------------------------------------------------------- - --- Code derived from the document "The Haskell 98 Foreign Function --- Interface, An Addendum to the Haskell 98 Report" is distributed under --- the following license: - --- Copyright (c) 2002 Manuel M. T. Chakravarty - --- The authors intend this Report to belong to the entire Haskell --- community, and so we grant permission to copy and distribute it for --- any purpose, provided that it is reproduced in its entirety, --- including this Notice. Modified versions of this Report may also be --- copied and distributed for any purpose, provided that the modified --- version is clearly presented as such, and that it does not claim to --- be a definition of the Haskell 98 Foreign Function Interface. - --- ----------------------------------------------------------------------------- - readEither :: Read a => String -> Either String a readEither s = case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of diff --git a/core/GhcMod/SrcUtils.hs b/core/GhcMod/SrcUtils.hs index 30999f3f1..97e00c0f2 100644 --- a/core/GhcMod/SrcUtils.hs +++ b/core/GhcMod/SrcUtils.hs @@ -42,6 +42,29 @@ instance HasType (LPat Id) where ---------------------------------------------------------------- +-- | Like `mkQ`, but matches on 2-parameter polymorphic type instead of a +-- monomorphic one. +-- +-- Idea shamelessly stolen from SPJ's talk on generic function extension +-- from Oxford's Workshop on Datatype-Generic programming (2004) +-- http://www.cs.ox.ac.uk/research/pdt/ap/dgp/workshop2004/ +mkQ2 :: (Data a, Typeable t) => r -> (forall b c. (Data b, Data c) => t b c -> r) -> a -> r +mkQ2 gen spec x = maybe gen (($ x) . unQ) $ dataCast2 (Q spec) +newtype Q r a = Q { unQ :: a -> r } + +findSpanName :: G.HsGroup G.Name -> (Int, Int) -> [(SrcSpan, [G.Name])] +findSpanName tcm lc = + everythingStaged Renamer (++) [] ([] `mkQ2` locateName) tcm + where + locateName :: (Data a, Data b) => GenLocated a b -> [(SrcSpan, [G.Name])] + locateName (L spn' x) + | Just spn <- cast spn' + , G.isGoodSrcSpan spn && spn `G.spans` lc + , names <- listifyStaged Renamer (const True) x + , not (null names) + = [(spn, names)] + | otherwise = [] + -- | Stores mapping from monomorphic to polymorphic types type CstGenQS = M.Map Var Type -- | Generic type to simplify SYB definition diff --git a/ghc-mod.cabal b/ghc-mod.cabal index e0b48232d..bd17430bf 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -7,7 +7,11 @@ Author: Kazu Yamamoto , Maintainer: Daniel Gröber License: AGPL-3 License-File: LICENSE -License-Files: COPYING.BSD3 COPYING.AGPL3 +License-Files: COPYING.BSD3 + COPYING.BSD3.ghc + COPYING.BSD3.base + COPYING.BSD3.haddock-api + COPYING.AGPL3 Homepage: https://github.com/DanielG/ghc-mod Synopsis: Happy Haskell Hacking Description: @@ -79,6 +83,7 @@ Extra-Source-Files: ChangeLog test/data/file-mapping/*.hs test/data/file-mapping/preprocessor/*.hs test/data/file-mapping/lhs/*.lhs + test/data/imported-from/*.hs test/data/nice-qualification/*.hs test/data/stack-project/stack.yaml.in test/data/stack-project/new-template.cabal @@ -103,7 +108,7 @@ Custom-Setup Library Default-Language: Haskell2010 - GHC-Options: -Wall -fno-warn-deprecations + GHC-Options: -Wall Default-Extensions: ScopedTypeVariables, RecordWildCards, NamedFieldPuns, ConstraintKinds, FlexibleContexts, DataKinds, KindSignatures, TypeOperators, ViewPatterns @@ -119,6 +124,7 @@ Library GhcMod.Exe.Find GhcMod.Exe.Flag GhcMod.Exe.Info + GhcMod.Exe.ImportedFrom GhcMod.Exe.Internal GhcMod.Exe.Lang GhcMod.Exe.Lint @@ -211,6 +217,15 @@ Library if impl(ghc < 7.8) Build-Depends: convertible < 1.2 && >= 1.1.0.0 + if impl(ghc >= 8.0) + Build-Depends: haddock-api < 2.18 + if impl(ghc >= 7.10 && < 8.0) + Build-Depends: haddock-api < 2.17 + if impl(ghc >= 7.8 && < 7.10) + Build-Depends: haddock-api < 2.16 + if impl(ghc < 7.8) + Build-Depends: haddock < 2.15.0 + Executable ghc-mod Default-Language: Haskell2010 Main-Is: GhcModMain.hs diff --git a/src/GhcMod/Exe/Options/Commands.hs b/src/GhcMod/Exe/Options/Commands.hs index cd8ef550a..2415f42d0 100644 --- a/src/GhcMod/Exe/Options/Commands.hs +++ b/src/GhcMod/Exe/Options/Commands.hs @@ -51,6 +51,7 @@ data GhcModCommands = | CmdDebugComponent [String] | CmdCheck [FilePath] | CmdExpand [FilePath] + | CmdImportedFrom FilePath Point (Maybe Expr) | CmdInfo FilePath Symbol | CmdType Bool FilePath Point | CmdSplit FilePath Point @@ -134,6 +135,9 @@ commands = <> command "expand" $$ info expandArgSpec $$ progDesc "Like `check' but also pass `-ddump-splices' to GHC" + <> command "imported-from" + $$ info importedFromArgSpec + $$ progDesc "Get the Haddock URL of the expression under (LINE,COL)" <> command "info" $$ info infoArgSpec $$ progDesc' $$$ do @@ -229,7 +233,7 @@ locArgSpec x = x modulesArgSpec, docArgSpec, findArgSpec, lintArgSpec, browseArgSpec, checkArgSpec, expandArgSpec, - infoArgSpec, typeArgSpec, autoArgSpec, splitArgSpec, + importedFromArgSpec, infoArgSpec, typeArgSpec, autoArgSpec, splitArgSpec, sigArgSpec, refineArgSpec, debugComponentArgSpec, mapArgSpec, unmapArgSpec, legacyInteractiveArgSpec :: Parser GhcModCommands @@ -269,6 +273,7 @@ browseArgSpec = CmdBrowse debugComponentArgSpec = filesArgsSpec (pure CmdDebugComponent) checkArgSpec = filesArgsSpec (pure CmdCheck) expandArgSpec = filesArgsSpec (pure CmdExpand) +importedFromArgSpec = locArgSpec (pure CmdImportedFrom) <*> optional (strArg "SYMBOL") infoArgSpec = CmdInfo <$> strArg "FILE" <*> strArg "SYMBOL" diff --git a/src/GhcModMain.hs b/src/GhcModMain.hs index d4c011661..710174d36 100644 --- a/src/GhcModMain.hs +++ b/src/GhcModMain.hs @@ -17,6 +17,7 @@ import Prelude import GhcMod import GhcMod.Exe.Find +import GhcMod.Exe.ImportedFrom import GhcMod.Exe.Options import GhcMod.Exe.Internal hiding (MonadIO,liftIO) import GhcMod.Monad @@ -147,6 +148,7 @@ ghcCommands (CmdLint opts file) = lint opts file ghcCommands (CmdBrowse opts ms) = concat <$> browse opts `mapM` ms ghcCommands (CmdCheck files) = checkSyntax files ghcCommands (CmdExpand files) = expandTemplate files +ghcCommands (CmdImportedFrom file (line, col) symb) = importedFrom file line col $ fmap Expression symb ghcCommands (CmdInfo file symb) = info file $ Expression symb ghcCommands (CmdType wCon file (line, col)) = types wCon file line col ghcCommands (CmdSplit file (line, col)) = splits file line col diff --git a/test/ImportedFromSpec.hs b/test/ImportedFromSpec.hs new file mode 100644 index 000000000..584f9ab06 --- /dev/null +++ b/test/ImportedFromSpec.hs @@ -0,0 +1,93 @@ +{-# LANGUAGE CPP #-} +module ImportedFromSpec where + +import Data.List +import Language.Haskell.GhcMod +import Test.Hspec +import TestUtils +import Prelude + +--------------------------------------------------- +import Language.Haskell.GhcMod.ImportedFrom +--------------------------------------------------- + +spec :: Spec +spec = do + let tdir = "test/data/imported-from" + + describe "checkImportedFrom" $ do + + -- Previously this test looked up the "Maybe" in a type signature + -- but now it fails - for some reason the expansion of spans + -- was giving the contents of the body of the function. This worked + -- before??? + it "can look up Maybe" $ do + res <- runD' tdir $ importedFrom "ImportedFrom01.hs" 22 17 (Just (Expression "Maybe")) + res `shouldSatisfy` (\x -> "base-" `isInfixOf` x || "haskell98-" `isInfixOf` x || "haskell2010-" `isInfixOf` x) + res `shouldSatisfy` (isInfixOf "Data-Maybe.html") + + it "can look up Just" $ do + res <- runD' tdir $ importedFrom "ImportedFrom01.hs" 12 7 (Just (Expression "Just")) + res `shouldSatisfy` (\x -> "base-" `isInfixOf` x || "haskell98-" `isInfixOf` x || "haskell2010-" `isInfixOf` x) + res `shouldSatisfy` (isInfixOf "Data-Maybe.html") + + it "can look up Just" $ do + res <- runD' tdir $ importedFrom "ImportedFrom01.hs" 16 10 (Just (Expression "Just")) + res `shouldSatisfy` (\x -> "base-" `isInfixOf` x || "haskell98-" `isInfixOf` x || "haskell2010-" `isInfixOf` x) + res `shouldSatisfy` (isInfixOf "Data-Maybe.html") + + it "can look up String" $ do + res <- runD' tdir $ importedFrom "ImportedFrom01.hs" 20 14 (Just (Expression "String")) + res `shouldSatisfy` (\x -> "base-" `isInfixOf` x || "haskell98-" `isInfixOf` x || "haskell2010-" `isInfixOf` x) + res `shouldSatisfy` (isInfixOf "Prelude.html") + + it "can look up Int" $ do + res <- runD' tdir $ importedFrom "ImportedFrom01.hs" 22 23 (Just (Expression "Int")) + res `shouldSatisfy` (\x -> "base-" `isInfixOf` x || "haskell98-" `isInfixOf` x || "haskell2010-" `isInfixOf` x) + res `shouldSatisfy` (isInfixOf "Prelude.html") + res `shouldSatisfy` (isPrefixOf "ghc-prim") + + it "can look up DL.length" $ do + res <- runD' tdir $ importedFrom "ImportedFrom01.hs" 23 5 (Just (Expression "DL.length")) + res `shouldSatisfy` (\x -> "base-" `isInfixOf` x || "haskell98-" `isInfixOf` x || "haskell2010-" `isInfixOf` x) + res `shouldSatisfy` (isInfixOf "Data-List.html") + + it "can look up print" $ do + res <- runD' tdir $ importedFrom "ImportedFrom01.hs" 25 8 (Just (Expression "print")) + res `shouldSatisfy` (\x -> "base-" `isInfixOf` x || "haskell98-" `isInfixOf` x || "haskell2010-" `isInfixOf` x) + res `shouldSatisfy` (isInfixOf "Prelude.html") + + it "can look up DM.fromList" $ do + res <- runD' tdir $ importedFrom "ImportedFrom01.hs" 27 5 (Just (Expression "DM.fromList")) + res `shouldSatisfy` (isInfixOf "containers-") + res `shouldSatisfy` (isInfixOf "Data-Map.html") + + -- This one is failing for some reason - something about not being able to load Safe? Temporarily disabling. + -- + -- Failed to load interface for \8216Safe\8217\nUse -v to see a list of the files searched for.\n + -- + --it "can look up Safe.headMay" $ do + -- withDirectory_ "test/data/imported-from" $ do + -- (res, _) <- runGmOutDef $ runGhcModT defaultOptions $ importedFrom "ImportedFrom01.hs" 29 6 (Just (Expression "Safe.headMay")) + -- res `shouldSatisfy` isRight + + it "can look up Foo.Bar.length" $ do + res <- runD' tdir $ importedFrom "ImportedFrom01.hs" 34 17 (Just (Expression "Foo.Bar.length")) + res `shouldSatisfy` (\x -> "base-" `isInfixOf` x || "haskell98-" `isInfixOf` x || "haskell2010-" `isInfixOf` x) + res `shouldSatisfy` (isInfixOf "Data-List.html") + + -- These from Safe also fail. Why? + --it "can look up map" $ do + -- res <- runD' tdir $ importedFrom "ImportedFrom02.hs" 14 5 (Just (Expression "map")) + -- res `shouldSatisfy` (isInfixOf "000") + -- res `shouldSatisfy` (isInfixOf "111") + + --it "can look up head" $ do + -- res <- runD' tdir $ importedFrom "ImportedFrom02.hs" 16 5 (Just (Expression "head")) + -- res `shouldSatisfy` (isInfixOf "000") + -- res `shouldSatisfy` (isInfixOf "111") + + it "can look up when" $ do + res <- runD' tdir $ importedFrom "ImportedFrom03.hs" 15 5 (Just (Expression "when")) + res `shouldSatisfy` (\x -> "base-" `isInfixOf` x || "haskell98-" `isInfixOf` x || "haskell2010-" `isInfixOf` x) + res `shouldSatisfy` (isInfixOf "Control-Monad.html") diff --git a/test/data/imported-from/ImportedFrom01.hs b/test/data/imported-from/ImportedFrom01.hs new file mode 100644 index 000000000..48059a96d --- /dev/null +++ b/test/data/imported-from/ImportedFrom01.hs @@ -0,0 +1,34 @@ +-- ImportedFrom01.hs + +module ImportedFrom01 where + +import Data.Maybe +import qualified Data.List as DL +import qualified Data.Map as DM +-- import qualified Safe +import qualified Data.List as Foo.Bar + +f :: a -> Maybe a +f x = Just x + +g :: IO () +g = do + let (Just _, _) = (Just 3, Just 4) + + return () + +s = "boo" :: String +s' = head s +t = Just 100 :: Maybe Int +r = DL.length [1, 2, 3] + +main = print "Hello, World!" + +h = DM.fromList [("x", "y")] + +-- sh = Safe.headMay [] + +i = 3 :: Int +i' = 3 :: Integer + +len = Foo.Bar.length diff --git a/test/data/imported-from/ImportedFrom02.hs b/test/data/imported-from/ImportedFrom02.hs new file mode 100644 index 000000000..afac299e8 --- /dev/null +++ b/test/data/imported-from/ImportedFrom02.hs @@ -0,0 +1,19 @@ +-- ImportedFrom02.hs + +module ImportedFrom02 where + +import Data.List hiding (map) +import System.Environment (getArgs) +import qualified Safe + + + + + + +m = map (+1) [1, 2, 3] + +h = head [1, 2, 3] + +h' = Safe.headMay [] + diff --git a/test/data/imported-from/ImportedFrom03.hs b/test/data/imported-from/ImportedFrom03.hs new file mode 100644 index 000000000..4fc6a7634 --- /dev/null +++ b/test/data/imported-from/ImportedFrom03.hs @@ -0,0 +1,40 @@ +-- ImportedFrom03.hs + +module ImportedFrom03 where + +import Control.Monad ( forM_, liftM, filterM, when, unless ) +-- import Control.Monad.Identity +-- import Control.Monad.Reader +-- import Control.Monad.Trans.Writer.Lazy + + + + + +main = do + when True $ do print "hah" + + +data Hello = Hello deriving Show + +foo = do + print "hello" + putStrLn "hello" + +{- + where + _ = (+) + _ = (-) + _ = (*) + _ = (/) + _ = True + _ = False + _ = (&&) + _ = (||) + _ = min :: Int -> Int -> Int + _ = max :: Int -> Int -> Int + _ = succ :: Int -> Int + _ = (++) + _ = (>) + _ = (==) +-}