Skip to content

Adding import in GHC plugin shows unused-import

Summary

In a compiler plugin, I'm hooking into parsedActionResult to modify the module being compiled. I'm adding an import and then using something from the import, but GHC still complains about unused-import.

Update 1: It seems like this happens because of generatedSrcSpan. See Reddit thread for more details.

More generally, is there a way to use an identifier without needing to import it? I know with Template Haskell, if you use a ticked Name, it'll resolve successfully, even if it's not imported.

Original Reddit post: https://www.reddit.com/r/haskell/comments/vbwuzk/writing_a_ghc_plugin_autogenerated_import/

Steps to reproduce

Minimal repro:

module Plugin (plugin) where

import GHC.Hs
import GHC.Plugins
import qualified GHC.Types.Name.Occurrence as NameSpace

plugin :: Plugin
plugin =
  defaultPlugin
    { parsedResultAction = \_ _ modl -> pure modl{hpm_module = update <$> hpm_module modl}
    }

update :: HsModule -> HsModule
update modl =
  modl
    { hsmodImports =
        [ genLoc $ simpleImportDecl $ mkModuleName "Data.List"
        ]
    , hsmodDecls =
        [ genLoc . genFuncDecl (mkRdrName "intercalate2") [] $
            genLoc $ HsVar NoExtField (mkRdrName "intercalate")
        ]
    }

-- | Make simple function declaration of the form `<funcName> <funcArgs> = <funcBody>`
genFuncDecl :: Located RdrName ->  [LPat GhcPs] -> LHsExpr GhcPs -> HsDecl GhcPs
genFuncDecl funcName funcArgs funcBody =
  (\body -> ValD NoExtField $ FunBind NoExtField funcName body [])
    . (\match -> MG NoExtField (genLoc [genLoc match]) Generated)
    . Match NoExtField (FunRhs funcName Prefix NoSrcStrict) funcArgs
    . (\grhs -> GRHSs NoExtField [genLoc grhs] funcWhere)
    $ GRHS NoExtField [] funcBody
  where
    funcWhere = genLoc $ EmptyLocalBinds NoExtField

genLoc :: e -> Located e
genLoc = L generatedSrcSpan

mkRdrName :: String -> Located RdrName
mkRdrName = genLoc . mkRdrUnqual . mkOccName NameSpace.varName
{-# OPTIONS_GHC -fplugin=Plugin #-}
module Foo where
stack ghc --resolver=ghc-9.0 --package ghc -- Foo.hs -Wunused-imports -Werror -dynamic-too

Expected behavior

This should generate the equivalent of

module Foo where
import Data.List
intercalate2 = intercalate

So Data.List is being used; indeed, when removing the import, the use of intercalate fails. So Data.List shouldn't be marked unused.

Environment

  • GHC version used: 9.0 (I haven't tested with 9.2 because I'm using the 9.0 version of the GHC API, and I didn't want to update it to 9.2 yet)

Optional:

  • Operating System:
  • System Architecture:
Edited by Brandon Chinn
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information