Fix Trac #2723: keep track of record field names in the renamer
[ghc-hetmet.git] / compiler / basicTypes / RdrName.lhs
index be60d03..31ffe6a 100644 (file)
@@ -4,34 +4,53 @@
 %
 
 \begin{code}
+
+-- |
+-- #name_types#
+-- GHC uses several kinds of name internally:
+--
+-- * 'OccName.OccName': see "OccName#name_types"
+--
+-- * 'RdrName.RdrName' is the type of names that come directly from the parser. They
+--   have not yet had their scoping and binding resolved by the renamer and can be
+--   thought of to a first approximation as an 'OccName.OccName' with an optional module
+--   qualifier
+--
+-- * 'Name.Name': see "Name#name_types"
+--
+-- * 'Id.Id': see "Id#name_types"
+--
+-- * 'Var.Var': see "Var#name_types"
 module RdrName (
+        -- * The main type
        RdrName(..),    -- Constructors exported only to BinIface
 
-       -- Construction
+       -- ** Construction
        mkRdrUnqual, mkRdrQual, 
        mkUnqual, mkVarUnqual, mkQual, mkOrig,
        nameRdrName, getRdrName, 
        mkDerivedRdrName, 
 
-       -- Destruction
-       rdrNameOcc, setRdrNameSpace,
+       -- ** Destruction
+       rdrNameOcc, rdrNameSpace, setRdrNameSpace,
        isRdrDataCon, isRdrTyVar, isRdrTc, isQual, isQual_maybe, isUnqual, 
        isOrig, isOrig_maybe, isExact, isExact_maybe, isSrcRdrName,
 
-       -- Printing;    instance Outputable RdrName
+       -- ** Printing
+       showRdrName,
 
-       -- LocalRdrEnv
+       -- * Local mapping of 'RdrName' to 'Name.Name'
        LocalRdrEnv, emptyLocalRdrEnv, extendLocalRdrEnv,
        lookupLocalRdrEnv, lookupLocalRdrOcc, elemLocalRdrEnv,
 
-       -- GlobalRdrEnv
+       -- * Global mapping of 'RdrName' to 'GlobalRdrElt's
        GlobalRdrEnv, emptyGlobalRdrEnv, mkGlobalRdrEnv, plusGlobalRdrEnv, 
        lookupGlobalRdrEnv, extendGlobalRdrEnv,
        pprGlobalRdrEnv, globalRdrEnvElts,
        lookupGRE_RdrName, lookupGRE_Name, getGRE_NameQualifier_maybes,
-        hideSomeUnquals,
+        hideSomeUnquals, findLocalDupsRdrEnv,
 
-       -- GlobalRdrElt, Provenance, ImportSpec
+       -- ** Global 'RdrName' mapping elements: 'GlobalRdrElt', 'Provenance', 'ImportSpec'
        GlobalRdrElt(..), isLocalGRE, unQualOK, qualSpecOK, unQualSpecOK,
        Provenance(..), pprNameProvenance,
        Parent(..), 
@@ -57,27 +76,37 @@ import Util
 %************************************************************************
 
 \begin{code}
-data RdrName 
+-- | Do not use the data constructors of RdrName directly: prefer the family
+-- of functions that creates them, such as 'mkRdrUnqual'
+data RdrName
   = Unqual OccName
-       -- Used for ordinary, unqualified occurrences 
+       -- ^ Used for ordinary, unqualified occurrences, e.g. @x@, @y@ or @Foo@.
+       -- Create such a 'RdrName' with 'mkRdrUnqual'
 
   | Qual ModuleName OccName
-       -- A qualified name written by the user in 
-       --  *source* code.  The module isn't necessarily 
+       -- ^ A qualified name written by the user in 
+       -- /source/ code.  The module isn't necessarily 
        -- the module where the thing is defined; 
-       -- just the one from which it is imported
+       -- just the one from which it is imported.
+       -- Examples are @Bar.x@, @Bar.y@ or @Bar.Foo@.
+       -- Create such a 'RdrName' with 'mkRdrQual'
 
   | Orig Module OccName
-       -- An original name; the module is the *defining* module.
+       -- ^ An original name; the module is the /defining/ module.
        -- This is used when GHC generates code that will be fed
        -- into the renamer (e.g. from deriving clauses), but where
-       -- we want to say "Use Prelude.map dammit".  
+       -- we want to say \"Use Prelude.map dammit\". One of these
+       -- can be created with 'mkOrig'
  
   | Exact Name
-       -- We know exactly the Name. This is used 
-       --  (a) when the parser parses built-in syntax like "[]" 
-       --      and "(,)", but wants a RdrName from it
-       --  (b) by Template Haskell, when TH has generated a unique name
+       -- ^ We know exactly the 'Name'. This is used:
+       --
+       --  (1) When the parser parses built-in syntax like @[]@
+       --      and @(,)@, but wants a 'RdrName' from it
+       --
+       --  (2) By Template Haskell, when TH has generated a unique name
+       --
+       -- Such a 'RdrName' can be created by using 'getRdrName' on a 'Name'
 \end{code}
 
 
@@ -94,19 +123,28 @@ rdrNameOcc (Unqual occ) = occ
 rdrNameOcc (Orig _ occ) = occ
 rdrNameOcc (Exact name) = nameOccName name
 
+rdrNameSpace :: RdrName -> NameSpace
+rdrNameSpace = occNameSpace . rdrNameOcc
+
 setRdrNameSpace :: RdrName -> NameSpace -> RdrName
--- This rather gruesome function is used mainly by the parser
--- When parsing                data T a = T | T1 Int
--- we parse the data constructors as *types* because of parser ambiguities,
--- so then we need to change the *type constr* to a *data constr*
+-- ^ This rather gruesome function is used mainly by the parser.
+-- When parsing:
+--
+-- > data T a = T | T1 Int
+--
+-- we parse the data constructors as /types/ because of parser ambiguities,
+-- so then we need to change the /type constr/ to a /data constr/
+--
+-- The exact-name case /can/ occur when parsing:
 --
--- The original-name case *can* occur when parsing
---             data [] a = [] | a : [a]
--- For the orig-name case we return an unqualified name.
+-- > data [] a = [] | a : [a]
+--
+-- For the exact-name case we return an original name.
 setRdrNameSpace (Unqual occ) ns = Unqual (setOccNameSpace ns occ)
 setRdrNameSpace (Qual m occ) ns = Qual m (setOccNameSpace ns occ)
 setRdrNameSpace (Orig m occ) ns = Orig m (setOccNameSpace ns occ)
-setRdrNameSpace (Exact n)    ns = Orig (nameModule n)
+setRdrNameSpace (Exact n)    ns = ASSERT( isExternalName n ) 
+                                 Orig (nameModule n)
                                       (setOccNameSpace ns (nameOccName n))
 \end{code}
 
@@ -122,9 +160,12 @@ mkOrig :: Module -> OccName -> RdrName
 mkOrig mod occ = Orig mod occ
 
 ---------------
-mkDerivedRdrName :: Name -> (OccName -> OccName) -> (RdrName)
+-- | Produce an original 'RdrName' whose module that of a parent 'Name' but its 'OccName'
+-- is derived from that of it's parent using the supplied function
+mkDerivedRdrName :: Name -> (OccName -> OccName) -> RdrName
 mkDerivedRdrName parent mk_occ
-  = mkOrig (nameModule parent) (mk_occ (nameOccName parent))
+  = ASSERT2( isExternalName parent, ppr parent )
+    mkOrig (nameModule parent) (mk_occ (nameOccName parent))
 
 ---------------
        -- These two are used when parsing source files
@@ -135,6 +176,8 @@ mkUnqual sp n = Unqual (mkOccNameFS sp n)
 mkVarUnqual :: FastString -> RdrName
 mkVarUnqual n = Unqual (mkVarOccFS n)
 
+-- | Make a qualified 'RdrName' in the given namespace and where the 'ModuleName' and
+-- the 'OccName' are taken from the first and second elements of the tuple respectively
 mkQual :: NameSpace -> (FastString, FastString) -> RdrName
 mkQual sp (m, n) = Qual (mkModuleNameFS m) (mkOccNameFS sp n)
 
@@ -215,6 +258,9 @@ instance OutputableBndr RdrName where
        | isTvOcc (rdrNameOcc n) = char '@' <+> ppr n
        | otherwise              = ppr n
 
+showRdrName :: RdrName -> String
+showRdrName r = showSDoc (ppr r)
+
 instance Eq RdrName where
     (Exact n1)           == (Exact n2)    = n1==n2
        -- Convert exact to orig
@@ -256,18 +302,15 @@ instance Ord RdrName where
     compare (Orig _ _)   _           = GT
 \end{code}
 
-
-
 %************************************************************************
 %*                                                                     *
                        LocalRdrEnv
 %*                                                                     *
 %************************************************************************
 
-A LocalRdrEnv is used for local bindings (let, where, lambda, case)
-It is keyed by OccName, because we never use it for qualified names.
-
 \begin{code}
+-- | This environment is used to store local bindings (@let@, @where@, lambda, @case@).
+-- It is keyed by OccName, because we never use it for qualified names
 type LocalRdrEnv = OccEnv Name
 
 emptyLocalRdrEnv :: LocalRdrEnv
@@ -291,7 +334,6 @@ elemLocalRdrEnv rdr_name env
   | otherwise        = False
 \end{code}
 
-
 %************************************************************************
 %*                                                                     *
                        GlobalRdrEnv
@@ -300,30 +342,43 @@ elemLocalRdrEnv rdr_name env
 
 \begin{code}
 type GlobalRdrEnv = OccEnv [GlobalRdrElt]
-       -- Keyed by OccName; when looking up a qualified name
-       -- we look up the OccName part, and then check the Provenance
-       -- to see if the appropriate qualification is valid.  This
-       -- saves routinely doubling the size of the env by adding both
-       -- qualified and unqualified names to the domain.
-       --
-       -- The list in the range is reqd because there may be name clashes
-       -- These only get reported on lookup, not on construction
-
-       -- INVARIANT: All the members of the list have distinct 
-       --            gre_name fields; that is, no duplicate Names
+-- ^ Keyed by 'OccName'; when looking up a qualified name
+-- we look up the 'OccName' part, and then check the 'Provenance'
+-- to see if the appropriate qualification is valid.  This
+-- saves routinely doubling the size of the env by adding both
+-- qualified and unqualified names to the domain.
+--
+-- The list in the codomain is required because there may be name clashes
+-- These only get reported on lookup, not on construction
+--
+-- INVARIANT: All the members of the list have distinct 
+--           'gre_name' fields; that is, no duplicate Names
+--
+-- INVARIANT: Imported provenance => Name is an ExternalName
+--           However LocalDefs can have an InternalName.  This
+--           happens only when type-checking a [d| ... |] Template
+--           Haskell quotation; see this note in RnNames
+--           Note [Top-level Names in Template Haskell decl quotes]
 
+-- | An element of the 'GlobalRdrEnv'
 data GlobalRdrElt 
   = GRE { gre_name :: Name,
          gre_par  :: Parent,
-         gre_prov :: Provenance        -- Why it's in scope
+         gre_prov :: Provenance        -- ^ Why it's in scope
     }
 
+-- | The children of a Name are the things that are abbreviated by the ".."
+--   notation in export lists.  Specifically:
+--     TyCon   Children are * data constructors
+--                          * record field ids
+--     Class   Children are * class operations
+-- Each child has the parent thing as its Parent
 data Parent = NoParent | ParentIs Name
              deriving (Eq)
 
 instance Outputable Parent where
    ppr NoParent     = empty
-   ppr (ParentIs n) = ptext SLIT("parent:") <> ppr n
+   ppr (ParentIs n) = ptext (sLit "parent:") <> ppr n
    
 
 plusParent :: Parent -> Parent -> Parent
@@ -334,11 +389,11 @@ plusParent p1 p2 = ASSERT2( p1 == p2, parens (ppr p1) <+> parens (ppr p2) )
 plusParent :: Parent -> Parent -> Parent
 plusParent NoParent     rel = 
   ASSERT2( case rel of { NoParent -> True; other -> False }, 
-          ptext SLIT("plusParent[NoParent]: ") <+> ppr rel )    
+          ptext (sLit "plusParent[NoParent]: ") <+> ppr rel )    
   NoParent
 plusParent (ParentIs n) rel = 
   ASSERT2( case rel of { ParentIs m -> n==m;  other -> False }, 
-          ptext SLIT("plusParent[ParentIs]:") <+> ppr n <> comma <+> ppr rel )
+          ptext (sLit "plusParent[ParentIs]:") <+> ppr n <> comma <+> ppr rel )
   ParentIs n
  -}
 
@@ -392,20 +447,24 @@ getGRE_NameQualifier_maybes env
         qualifier_maybe (Imported iss) = Just $ map (is_as . is_decl) iss 
 
 pickGREs :: RdrName -> [GlobalRdrElt] -> [GlobalRdrElt]
--- Take a list of GREs which have the right OccName
+-- ^ Take a list of GREs which have the right OccName
 -- Pick those GREs that are suitable for this RdrName
 -- And for those, keep only only the Provenances that are suitable
 -- 
--- Consider
+-- Consider:
+--
+-- @
 --      module A ( f ) where
 --      import qualified Foo( f )
 --      import Baz( f )
 --      f = undefined
--- Let's suppose that Foo.f and Baz.f are the same entity really.
--- The export of f is ambiguous because it's in scope from the local def
--- and the import.  The lookup of (Unqual f) should return a GRE for
--- the locally-defined f, and a GRE for the imported f, with a *single* 
--- provenance, namely the one for Baz(f).
+-- @
+--
+-- Let's suppose that @Foo.f@ and @Baz.f@ are the same entity really.
+-- The export of @f@ is ambiguous because it's in scope from the local def
+-- and the import.  The lookup of @Unqual f@ should return a GRE for
+-- the locally-defined @f@, and a GRE for the imported @f@, with a /single/ 
+-- provenance, namely the one for @Baz(f)@.
 pickGREs rdr_name gres
   = mapCatMaybes pick gres
   where
@@ -414,16 +473,17 @@ pickGREs rdr_name gres
 
     pick :: GlobalRdrElt -> Maybe GlobalRdrElt
     pick gre@(GRE {gre_prov = LocalDef, gre_name = n})         -- Local def
-       | rdr_is_unqual                         = Just gre
-       | Just (mod,_) <- rdr_is_qual, 
-         mod == moduleName (nameModule n)      = Just gre
-       | otherwise                             = Nothing
+       | rdr_is_unqual                    = Just gre
+       | Just (mod,_) <- rdr_is_qual           -- Qualified name
+       , Just n_mod <- nameModule_maybe n   -- Binder is External
+       , mod == moduleName n_mod          = Just gre
+       | otherwise                        = Nothing
     pick gre@(GRE {gre_prov = Imported [is]})  -- Single import (efficiency)
        | rdr_is_unqual,
-         not (is_qual (is_decl is))            = Just gre
+         not (is_qual (is_decl is))    = Just gre
        | Just (mod,_) <- rdr_is_qual, 
-         mod == is_as (is_decl is)             = Just gre
-       | otherwise                             = Nothing
+         mod == is_as (is_decl is)     = Just gre
+       | otherwise                     = Nothing
     pick gre@(GRE {gre_prov = Imported is})    -- Multiple import
        | null filtered_is = Nothing
        | otherwise        = Just (gre {gre_prov = Imported filtered_is})
@@ -440,18 +500,10 @@ isLocalGRE (GRE {gre_prov = LocalDef}) = True
 isLocalGRE _                           = False
 
 unQualOK :: GlobalRdrElt -> Bool
--- An unqualifed version of this thing is in scope
+-- ^ Test if an unqualifed version of this thing would be in scope
 unQualOK (GRE {gre_prov = LocalDef})    = True
 unQualOK (GRE {gre_prov = Imported is}) = any unQualSpecOK is
 
-unQualSpecOK :: ImportSpec -> Bool
--- In scope unqualified
-unQualSpecOK is = not (is_qual (is_decl is))
-
-qualSpecOK :: ModuleName -> ImportSpec -> Bool
--- In scope qualified with M
-qualSpecOK mod is = mod == is_as (is_decl is)
-
 plusGlobalRdrEnv :: GlobalRdrEnv -> GlobalRdrEnv -> GlobalRdrEnv
 plusGlobalRdrEnv env1 env2 = plusOccEnv_C (foldr insertGRE) env1 env2
 
@@ -463,6 +515,27 @@ mkGlobalRdrEnv gres
                                 (nameOccName (gre_name gre)) 
                                 [gre]
 
+findLocalDupsRdrEnv :: GlobalRdrEnv -> [OccName] -> (GlobalRdrEnv, [[Name]])
+-- ^ For each 'OccName', see if there are multiple local definitions
+-- for it.  If so, remove all but one (to suppress subsequent error messages)
+-- and return a list of the duplicate bindings
+findLocalDupsRdrEnv rdr_env occs 
+  = go rdr_env [] occs
+  where
+    go rdr_env dups [] = (rdr_env, dups)
+    go rdr_env dups (occ:occs)
+      = case filter isLocalGRE gres of
+         []       -> WARN( True, ppr occ <+> ppr rdr_env ) 
+                     go rdr_env dups occs      -- Weird!  No binding for occ
+         [_]      -> go rdr_env dups occs      -- The common case
+         dup_gres -> go (extendOccEnv rdr_env occ (head dup_gres : nonlocal_gres))
+                        (map gre_name dup_gres : dups)
+                        occs
+      where
+        gres = lookupOccEnv rdr_env occ `orElse` []
+       nonlocal_gres = filterOut isLocalGRE gres
+
+
 insertGRE :: GlobalRdrElt -> [GlobalRdrElt] -> [GlobalRdrElt]
 insertGRE new_g [] = [new_g]
 insertGRE new_g (old_g : old_gs)
@@ -479,12 +552,15 @@ plusGRE g1 g2
          gre_par  = gre_par  g1 `plusParent` gre_par  g2 }
 
 hideSomeUnquals :: GlobalRdrEnv -> [OccName] -> GlobalRdrEnv
--- Hide any unqualified bindings for the specified OccNames
+-- ^ Hide any unqualified bindings for the specified OccNames
 -- This is used in TH, when renaming a declaration bracket
---     [d| foo = ... |]
--- We want unqualified 'foo' in "..." to mean this foo, not
--- the one from the enclosing module.  But the *qualified* name
--- from the enclosing moudule must certainly still be avaialable
+--
+-- > [d| foo = ... |]
+--
+-- We want unqualified @foo@ in "..." to mean this @foo@, not
+-- the one from the enclosing module.  But the /qualified/ name
+-- from the enclosing module must certainly still be available
+
 --     Seems like 5 times as much work as it deserves!
 hideSomeUnquals rdr_env occs
   = foldr hide rdr_env occs
@@ -495,7 +571,7 @@ hideSomeUnquals rdr_env occs
     qual_gre gre@(GRE { gre_name = name, gre_prov = LocalDef })
        = gre { gre_prov = Imported [imp_spec] }
        where   -- Local defs get transfomed to (fake) imported things
-         mod = moduleName (nameModule name)
+         mod = ASSERT2( isExternalName name, ppr name) moduleName (nameModule name)
          imp_spec = ImpSpec { is_item = ImpAll, is_decl = decl_spec }
          decl_spec = ImpDeclSpec { is_mod = mod, is_as = mod, 
                                    is_qual = True, 
@@ -508,53 +584,66 @@ hideSomeUnquals rdr_env occs
        = spec { is_decl = decl_spec { is_qual = True } }
 \end{code}
 
-
 %************************************************************************
 %*                                                                     *
                        Provenance
 %*                                                                     *
 %************************************************************************
 
-The "provenance" of something says how it came to be in scope.
-It's quite elaborate so that we can give accurate unused-name warnings.
-
 \begin{code}
+-- | The 'Provenance' of something says how it came to be in scope.
+-- It's quite elaborate so that we can give accurate unused-name warnings.
 data Provenance
-  = LocalDef           -- Defined locally
-  | Imported           -- Imported
-       [ImportSpec]    -- INVARIANT: non-empty
+  = LocalDef           -- ^ The thing was defined locally
+  | Imported           
+       [ImportSpec]    -- ^ The thing was imported.
+                       -- 
+                       -- INVARIANT: the list of 'ImportSpec' is non-empty
 
 data ImportSpec = ImpSpec { is_decl :: ImpDeclSpec,
                            is_item ::  ImpItemSpec }
                deriving( Eq, Ord )
 
-data ImpDeclSpec       -- Describes a particular import declaration
-                       -- Shared among all the Provenaces for that decl
+-- | Describes a particular import declaration and is
+-- shared among all the 'Provenance's for that decl
+data ImpDeclSpec
   = ImpDeclSpec {
-       is_mod      :: ModuleName, -- 'import Muggle'
-                               -- Note the Muggle may well not be 
-                               -- the defining module for this thing!
-                                -- TODO: either should be Module, or there
-                                -- should be a Maybe PackageId here too.
-       is_as       :: ModuleName, -- 'as M' (or 'Muggle' if there is no 'as' clause)
-       is_qual     :: Bool,    -- True <=> qualified (only)
-       is_dloc     :: SrcSpan  -- Location of import declaration
+       is_mod      :: ModuleName, -- ^ Module imported, e.g. @import Muggle@
+                                  -- Note the @Muggle@ may well not be 
+                                  -- the defining module for this thing!
+
+                                   -- TODO: either should be Module, or there
+                                   -- should be a Maybe PackageId here too.
+       is_as       :: ModuleName, -- ^ Import alias, e.g. from @as M@ (or @Muggle@ if there is no @as@ clause)
+       is_qual     :: Bool,       -- ^ Was this import qualified?
+       is_dloc     :: SrcSpan     -- ^ The location of the import declaration
     }
 
-data ImpItemSpec  -- Describes import info a particular Name
-  = ImpAll             -- The import had no import list, 
-                       -- or  had a hiding list
+-- | Describes import info a particular Name
+data ImpItemSpec
+  = ImpAll             -- ^ The import had no import list, 
+                       -- or had a hiding list
 
-  | ImpSome {          -- The import had an import list
+  | ImpSome {
        is_explicit :: Bool,
        is_iloc     :: SrcSpan  -- Location of the import item
-    }
-       -- The is_explicit field is True iff the thing was named 
-       -- *explicitly* in the import specs rather 
-       -- than being imported as part of a "..." group 
-       -- e.g.         import C( T(..) )
-       -- Here the constructors of T are not named explicitly; 
-       -- only T is named explicitly.
+    }   -- ^ The import had an import list.
+       -- The 'is_explicit' field is @True@ iff the thing was named 
+       -- /explicitly/ in the import specs rather
+       -- than being imported as part of a "..." group. Consider:
+       --
+       -- > import C( T(..) )
+       --
+       -- Here the constructors of @T@ are not named explicitly; 
+       -- only @T@ is named explicitly.
+
+unQualSpecOK :: ImportSpec -> Bool
+-- ^ Is in scope unqualified?
+unQualSpecOK is = not (is_qual (is_decl is))
+
+qualSpecOK :: ModuleName -> ImportSpec -> Bool
+-- ^ Is in scope qualified with the given module?
+qualSpecOK mod is = mod == is_as (is_decl is)
 
 importSpecLoc :: ImportSpec -> SrcSpan
 importSpecLoc (ImpSpec decl ImpAll) = is_dloc decl
@@ -608,9 +697,9 @@ plusProv _               LocalDef        = LocalDef
 plusProv (Imported is1)  (Imported is2)  = Imported (is1++is2)
 
 pprNameProvenance :: GlobalRdrElt -> SDoc
--- Print out the place where the name was imported
+-- ^ Print out the place where the name was imported
 pprNameProvenance (GRE {gre_name = name, gre_prov = LocalDef})
-  = ptext SLIT("defined at") <+> ppr (nameSrcLoc name)
+  = ptext (sLit "defined at") <+> ppr (nameSrcLoc name)
 pprNameProvenance (GRE {gre_name = name, gre_prov = Imported whys})
   = case whys of
        (why:_) -> sep [ppr why, nest 2 (ppr_defn (nameSrcLoc name))]
@@ -619,13 +708,13 @@ pprNameProvenance (GRE {gre_name = name, gre_prov = Imported whys})
 -- If we know the exact definition point (which we may do with GHCi)
 -- then show that too.  But not if it's just "imported from X".
 ppr_defn :: SrcLoc -> SDoc
-ppr_defn loc | isGoodSrcLoc loc = parens (ptext SLIT("defined at") <+> ppr loc)
+ppr_defn loc | isGoodSrcLoc loc = parens (ptext (sLit "defined at") <+> ppr loc)
             | otherwise        = empty
 
 instance Outputable ImportSpec where
    ppr imp_spec
-     = ptext SLIT("imported from") <+> ppr (importSpecModule imp_spec) 
-       <+> if isGoodSrcSpan loc then ptext SLIT("at") <+> ppr loc
+     = ptext (sLit "imported from") <+> ppr (importSpecModule imp_spec) 
+       <+> if isGoodSrcSpan loc then ptext (sLit "at") <+> ppr loc
                                 else empty
      where
        loc = importSpecLoc imp_spec