BSD-3-Clause licensed by Philipp Schuster, Roman Cheplyaka, Lennart Augustsson
Maintained by Philipp Schuster

haskell-names

haskell-names does name and module resolution for haskell-src-exts AST.

Namely, it can do the following:

  • for a module, compute its interface, i.e. the set of entities exported by the module, together with their original names.
  • for each name in the module, figure out what it refers to — whether it’s bound locally (say, by a where clause) or globally (and then give its origin).

Installation

To install a released version:

  1. Install Cabal and cabal-install from the git repository (the master branch)
  2. cabal install haskell-names hs-gen-iface

If you’re building a development version, then you might also need to install development versions of haskell-src-exts, haskell-packages, and hse-cpp.

Module interfaces

hs-gen-iface is a «compiler» that generates interfaces for Haskell modules.

An interface is a JSON file that lists all entities (types, classes, functions etc.) exported by every module. For example, here are a couple of entries from Prelude.names:

[
  {
    "name": "map",
    "entity": "value",
    "module": "GHC.Base"
  },
  {
    "name": "IO",
    "entity": "newtype",
    "module": "GHC.Types"
  },
  ...
]

As you see, each entity is annotated with the module where it was originally defined. Additionally, class methods, field selectors, and data constructors are annotated with the class or type they belong to.

Generating interfaces

Thanks to haskell-packages, hs-gen-iface is fully integrated with Cabal. To produce and install interface files, pass --haskell-suite -w hs-gen-iface flags to cabal install, for instance

cabal install --haskell-suite -w hs-gen-iface mtl

This assumes that the hs-gen-iface executable is in your PATH. You can specify the full path to hs-gen-iface after -w, too.

Core packages

haskell-names comes with the global package database populated with some core packages:

% hs-gen-iface pkg list --global
array-0.4.0.2
base-4.7.0.0
integer-simple-0.1.1.0
ghc-prim-0.3.1.0

Compiling core packages by hand

Suppose you need to compile any of the core packages by hand — for example, to get a different version than the one bundled with haskell-names.

Core packages, such as ghc-prim, integer-simple, array, and base, are highly GHC-specific and need to be tweaked a bit before they can be processed by haskell-names. Get our modified versions:

Note that Cabal’s new dependency solver won’t let you install ghc-prim or base easily. There are two ways to work around this:

  1. Use the old solver:

     cabal install --haskell-suite -w hs-gen-iface --solver=topdown
    
  2. Invoke all the steps manually:

     cabal configure --haskell-suite -w hs-gen-iface
     cabal build
     cabal install --only
    

Using interfaces

You can parse interface files directly, but a better idea is to use Distribution.HaskellSuite.Packages API (from haskell-packages), combined with the package database NamesDB defined in Language.Haskell.Modules.Interfaces.

Name resolution

The annotateModule function annotates the module with scoping information.

Its essence is described in the article Open your name resolution.

Example

Let’s say you have a module and you want to find out whether it uses Prelude.head.

module Main where

import Language.Haskell.Exts.Annotated
import qualified Language.Haskell.Exts as UnAnn (Name(Ident))
import Language.Haskell.Names
import Language.Haskell.Names.Interfaces
import Distribution.HaskellSuite
import Distribution.Simple.Compiler

import Data.Maybe
import Data.List
import Data.Proxy
import qualified Data.Foldable as Foldable
import Text.Printf
import Control.Applicative
import Control.Monad

main :: IO ()
main = do

  -- read the program's source from stdin
  source <- getContents

  let
    -- parse the program (using haskell-src-exts)
    ast = fromParseResult $
      parseModuleWithMode defaultParseMode {parseFilename="stdin"} source

  -- get all installed packages (user and global)
  pkgs <-
    (++) <$>
      getInstalledPackages (Proxy :: Proxy NamesDB) UserPackageDB <*>
      getInstalledPackages (Proxy :: Proxy NamesDB) GlobalPackageDB

  headUsages <- evalNamesModuleT (findHeads ast) pkgs

  forM_ headUsages $ \loc ->
    printf "Prelude.head is used at %s\n" (prettyPrint $ srcInfoSpan loc)

  when (null headUsages) $
    printf "Congratulations! Your code doesn't use Prelude.head\n"

-- | The `findHeads` function finds all occurrences of the `head` symbol in
-- a given AST of a Haskell module. It needs access to stored name information
-- and therefore runs in `ModuleT`.
    findHeads :: Module SrcSpanInfo -> ModuleT [Symbol] IO [SrcSpanInfo]
    findHeads ast = do

-- First we get all symbols exported from `Prelude` with `getModuleInfo`.
      symbols <- fromMaybe (error "Prelude not found") <$>
        getModuleInfo "Prelude"

-- Then we filter those for the one with name `"head"`.
      let
        headSymbol =
          fromMaybe (error "Prelude.head not found") (listToMaybe (do
            symbol <- symbols
            guard (symbolName symbol == UnAnn.Ident "head")
            return symbol))

-- We annotate the given ast.
      annotatedAst <-
        annotateModule
          Haskell2010 -- base language
          []          -- set of extensions
          ast

-- We get a list of all annotations from the annotated module.
      let
        annotations = Foldable.toList annotatedAst

-- A `GlobalSymbol` annotation means that the annotated name refers to a
-- global symbol. It also contains the qualified name that corresponds
-- to how it is referenced but that is not needed here.
        headUsages = nub (do
          Scoped (GlobalSymbol globalSymbol _) location <- annotations
          guard (globalSymbol == headSymbol)
          return location)

-- And finally we return all found usages.
     return headUsages

Example invocation

% ./find-heads 
one = head [1]
^D
Prelude.head is used at stdin: (1:7) - (1:11)

% ./find-heads
import Prelude hiding (head)
import Data.Text

f = head (pack "foo")
^D
Congratulations! Your code doesn't use Prelude.head

API documentation

See haskell-names haddock documentation.

The two modules you need are:

Other modules are more experimental, less documented, and you probably don’t need them anyway.

Known issues

See the list of all issues.

  • Because a non-trivial amount of packages are not designed to work with anything except GHC, hs-gen-iface currently pretends to be GHC. This is of course not acceptable — contributions here are welcome. (#32)
  • haskell-names doesn’t perform validation yet. If a module is not valid Haskell, then the behaviour is undefined. See the issues marked as validation.
  • Symbol fixities are not recorded (#1)
  • Type variables are not resolved (#2)
  • Arrows are not fully supported (#8)

Maintainers

Philipp Schuster is the primary maintainer.

Adam Bergmark is the backup maintainer. Please get in touch with him if the primary maintainer cannot be reached.

Changes

Changes

Version 0.5.2

  • Handle more syntactic constructs

Version 0.5.1

  • Resolve associated types
  • Resolve fixity declarations
  • Resolve classes and instances
  • Various bugfixes

Version 0.5.0

  • Unify type-level and value-level symbols
  • Remove fixities from symbol type
  • Properly annotate classes and instances
  • Inline original name into symbol type
  • Remove original package from symbol type
  • Annotate symbol references with the way they are referenced

Version 0.4.1

  • Export types defined by top level data family declarations
  • Update to haskell-src-exts 1.16

Version 0.4

Replace data-lens with data-lens-light

Version 0.3.3.2

Remove the upper version bound on Cabal

Version 0.3.3.1

Update to work with haskell-src-exts 1.15

Version 0.3.3

  • Expose Language.Haskell.Names.ModuleSymbols.getTopDecls
  • Define a Monoid instance for LocalSymbolTable.Table
  • Support for parallel list comprehensions

Version 0.3.2.8

Introduce a lower dependency bound on tasty-golden in the test suite

Version 0.3.2.7

type-eq is fixed; depend on the new version

Version 0.3.2.6

Work around a regression in type-eq

Version 0.3.2.5

Relax pretty-show version bound

Version 0.3.2.4

Make haskell-names build GHC 7.8

Version 0.3.2.3

Include interfaces for array

Version 0.3.2.2

Allow pretty-show-1.6.2 in the test suite

Version 0.3.2.1

Use pretty-show-1.6.1 in the test suite

Version 0.3.2

  • Export getDeclHead from Language.Haskell.Names.SyntaxUtils
  • Annotate QNames in export lists
  • Update the bundled library interfaces (they were broken in earlier 0.3.* versions because of the interface format change)

Version 0.3.1

  • Documentation improvements
  • Add rfoldMap
  • Relax Cabal dependency constraint to include Cabal-1.14

Version 0.3

This release brings support for record puns and wildcards.

Interface file format changes

For field selectors, the new field constructors is added. It contains a list of constructors that contain that field.

API changes

  • The sv_constructors field is added to SymSelector
  • Add Language.Haskell.Names.SyntaxUtils.stringToName
  • The class GetBound is moved to a new module, Language.Haskell.Names.GetBound. Its method, getBound, now has a new argument, the global symbol table
  • NameInfo got two more constructors to annotate wildcards, RecPatWildcard and RecExpWildcard
  • Scope now has a new field of type WcNames, which can be accessed through the wcNames lens. This is needed for record wildcards resolution.
  • Add field selectors to GName
  • Don’t export GName and OrigName from GlobalSymbolTable

Version 0.2.1

  • Fix a bug where global symbols were annotated as local
  • Make the code compile with aeson-0.6.2.0

Version 0.2

  • Proper annotations for types and binding sites. NameContext now has a few new constructors.
  • Properly support n+k patterns
  • Expose the Alg datatype for open name resolution
  • Expose the HasOrigName class

Version 0.1.2

Restore compatibility with Cabal 1.16

Version 0.1.1

Dummy release to force rebuild on hackage (now that haskell-src-exts 1.14 is released).