-- | Warn about unused imports.
--
-- For each @open@ statement, we want to issue a warning about concrete names
-- and modules brought into scope by this statement which are not referenced subsequently.
--
-- To this end, whenever we lookup a concrete name during scope checking,
-- we mark it as used by calling 'lookedupName' with the results of the lookup,
-- which is an 'AbstractName' or several 'AbstractName's in case the name
-- is ambiguous (e.g. an ambiguous constructor or projection).
-- Likewise, whenever we resolve a concrete module name,
-- we mark the resulting 'AbstractModule' as used by calling 'lookedupModule'.
-- If the concrete name is qualified, e.g. @M.x@,
-- we also mark the module @M@ as used that the qualifier resolved to.
--
-- We also record for each opened module the set of 'AbstractName's
-- and 'AbstractModule's it brought into scope.
--
-- When checking the file is done, we can traverse the each opened module
-- and report all the 'AbstractName's and 'AbstractModule's that we not used.

module Agda.Syntax.Scope.UnusedImports
  ( lookedupName
  , lookedupModule
  , registerModuleOpening
  , warnUnusedImports
  ) where

import Prelude hiding (null, (||))

import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IntMap
import Data.List (partition, sortOn)
import Data.Map qualified as Map
import Data.Set qualified as Set

import Agda.Interaction.Options (optQualifiedInstances)
import Agda.Interaction.Options.Warnings (WarningName (UnusedImports_, UnusedImportsAll_))

import Agda.Syntax.Abstract.Name
    ( WhyInScope(Defined, Opened, Applied),
      AbstractName, anameName, anameLineage,
      AbstractModule, amodName, amodLineage )
import Agda.Syntax.Abstract.Name qualified as A
import Agda.Syntax.Common
  ( IsInstanceDef(isInstanceDef), KwRange
  , ImportDirective'(using, impRenaming, publicOpen)
  , ImportedName'(ImportedName, ImportedModule), fromImportedName, Renaming'(renTo), Using'(Using, UseEverything)
  )
import Agda.Syntax.Common.Pretty (prettyShow, Pretty (pretty))
import Agda.Syntax.Concrete qualified as C
import Agda.Syntax.Position ( HasRange(getRange), SetRange(setRange) )
import Agda.Syntax.Position qualified as P
import Agda.Syntax.Scope.Base as A
import Agda.Syntax.Scope.State ( ScopeM, withCurrentModule )
-- importing Agda.Syntax.Scope.Monad creates import cycles

import Agda.TypeChecking.Monad.Base
import Agda.TypeChecking.Monad.Debug ( MonadDebug, reportSLn, __IMPOSSIBLE_VERBOSE__ )
import Agda.TypeChecking.Monad.State ( getScope )
import Agda.TypeChecking.Monad.Trace (setCurrentRange)
import Agda.TypeChecking.Warnings (warning)

import Agda.Utils.Boolean  ( (||) )
import Agda.Utils.Function ( applyUnless )
import Agda.Utils.Lens     ( (<&>), Lens' )
import Agda.Utils.List     ( partitionMaybe, hasElem )
import Agda.Utils.List1    ( pattern (:|), List1 )
import Agda.Utils.List1    qualified as List1
import Agda.Utils.List2    ( List2(..) )
import Agda.Utils.List2    qualified as List2
import Agda.Utils.Maybe    ( fromMaybe, isJust, mapMaybe, whenNothing )
import Agda.Utils.Monad    ( forM_, when, unless, whenM )
import Agda.Utils.Null     ( Null(null) )

import Agda.Utils.Impossible

-- | Call these whenever a concrete name was translated to an abstract one.
lookedupName ::
     C.QName       -- ^ The concrete name resolved by the scope checker.
  -> ResolvedName  -- ^ The resolution of the name.
  -> ScopeM ()
lookedupName :: QName -> ResolvedName -> ScopeM ()
lookedupName QName
x = \case
    DefinedName Access
_access AbstractName
y Suffix
_suffix -> AbstractName -> ScopeM ()
unamb AbstractName
y ScopeM () -> ScopeM () -> ScopeM ()
forall a b. TCMT IO a -> TCMT IO b -> TCMT IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> QName -> [AbstractName] -> ScopeM ()
forall a. InScope a => QName -> [a] -> ScopeM ()
lookedupQualifier QName
x [AbstractName
y]
    FieldName NonEmpty AbstractName
ys                  -> NonEmpty AbstractName -> ScopeM ()
add NonEmpty AbstractName
ys
    ConstructorName Set1 Induction
_ind NonEmpty AbstractName
ys       -> NonEmpty AbstractName -> ScopeM ()
add NonEmpty AbstractName
ys
    PatternSynResName NonEmpty AbstractName
ys          -> NonEmpty AbstractName -> ScopeM ()
add NonEmpty AbstractName
ys
    VarName{}                     -> () -> ScopeM ()
forall a. a -> TCMT IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
    UnknownName{}                 -> () -> ScopeM ()
forall a. a -> TCMT IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
  where
    add :: NonEmpty AbstractName -> ScopeM ()
add NonEmpty AbstractName
ys = do
      case NonEmpty AbstractName
ys of
        AbstractName
y  :| []      -> AbstractName -> ScopeM ()
unamb AbstractName
y
        AbstractName
y1 :| AbstractName
y2 : [AbstractName]
ys -> List2 AbstractName -> ScopeM ()
amb (List2 AbstractName -> ScopeM ())
-> List2 AbstractName -> ScopeM ()
forall a b. (a -> b) -> a -> b
$ AbstractName
-> AbstractName -> [AbstractName] -> List2 AbstractName
forall a. a -> a -> [a] -> List2 a
List2 AbstractName
y1 AbstractName
y2 [AbstractName]
ys
      QName -> [AbstractName] -> ScopeM ()
forall a. InScope a => QName -> [a] -> ScopeM ()
lookedupQualifier QName
x ([AbstractName] -> ScopeM ()) -> [AbstractName] -> ScopeM ()
forall a b. (a -> b) -> a -> b
$ NonEmpty AbstractName -> [Item (NonEmpty AbstractName)]
forall l. IsList l => l -> [Item l]
List1.toList NonEmpty AbstractName
ys
    unamb :: AbstractName -> ScopeM ()
unamb = Lens' TCState [AbstractName]
-> ([AbstractName] -> [AbstractName]) -> ScopeM ()
forall (m :: * -> *) a.
MonadTCState m =>
Lens' TCState a -> (a -> a) -> m ()
modifyTCLens ([AbstractName] -> f [AbstractName]) -> TCState -> f TCState
Lens' TCState [AbstractName]
stUnambiguousLookups (([AbstractName] -> [AbstractName]) -> ScopeM ())
-> (AbstractName -> [AbstractName] -> [AbstractName])
-> AbstractName
-> ScopeM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (:)
    amb :: List2 AbstractName -> ScopeM ()
amb List2 AbstractName
xs = case QName -> Maybe Int
forall a. HasRange a => a -> Maybe Int
rangeToPosPos QName
x of
      -- Andreas, 2025-11-30
      -- It can happen that a concrete identifier has no range,
      -- e.g. when it comes from an expanded ellipsis.
      -- In this case, we do not record the lookup,
      -- since it should have been looked up already
      -- when processing the pattern from the original lhs
      -- (that was duplicated by ellipsis expansion).
      -- See test/Interaction/ExpandEllipsis.
      Maybe Int
Nothing -> () -> ScopeM ()
forall a. a -> TCMT IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
      Just Int
i -> Lens' TCState (IntMap (List2 AbstractName))
-> (IntMap (List2 AbstractName) -> IntMap (List2 AbstractName))
-> ScopeM ()
forall (m :: * -> *) a.
MonadTCState m =>
Lens' TCState a -> (a -> a) -> m ()
modifyTCLens (IntMap (List2 AbstractName) -> f (IntMap (List2 AbstractName)))
-> TCState -> f TCState
Lens' TCState (IntMap (List2 AbstractName))
stAmbiguousLookups ((IntMap (List2 AbstractName) -> IntMap (List2 AbstractName))
 -> ScopeM ())
-> (IntMap (List2 AbstractName) -> IntMap (List2 AbstractName))
-> ScopeM ()
forall a b. (a -> b) -> a -> b
$ Int
-> List2 AbstractName
-> IntMap (List2 AbstractName)
-> IntMap (List2 AbstractName)
forall a. Int -> a -> IntMap a -> IntMap a
IntMap.insert Int
i List2 AbstractName
xs

-- | Call this whenever a concrete module name was translated to an abstract one.
lookedupModule ::
     C.QName         -- ^ The concrete module name resolved by the scope checker.
  -> AbstractModule  -- ^ The resolution of the module name.
  -> ScopeM ()
lookedupModule :: QName -> AbstractModule -> ScopeM ()
lookedupModule QName
x AbstractModule
m = do
  Lens' TCState [AbstractModule]
-> ([AbstractModule] -> [AbstractModule]) -> ScopeM ()
forall (m :: * -> *) a.
MonadTCState m =>
Lens' TCState a -> (a -> a) -> m ()
modifyTCLens ([AbstractModule] -> f [AbstractModule]) -> TCState -> f TCState
Lens' TCState [AbstractModule]
stModuleLookups (AbstractModule
m AbstractModule -> [AbstractModule] -> [AbstractModule]
forall a. a -> [a] -> [a]
:)
  QName -> [AbstractModule] -> ScopeM ()
forall a. InScope a => QName -> [a] -> ScopeM ()
lookedupQualifier QName
x [AbstractModule
m]

-- | If the concrete name @x@ is qualified, i.e., of the form @M.y@,
--   mark the modules @M@ as used through which @x@ resolved to (one of) the given things.
lookedupQualifier :: InScope a => C.QName -> [a] -> ScopeM ()
lookedupQualifier :: forall a. InScope a => QName -> [a] -> ScopeM ()
lookedupQualifier QName
x [a]
ys = case QName
x of
  C.QName{} -> () -> ScopeM ()
forall a. a -> TCMT IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
  C.Qual{}  -> TCMT IO Bool -> ScopeM () -> ScopeM ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
whenM TCMT IO Bool
unusedImportsEnabled do
    ms <- QName -> [a] -> ScopeInfo -> [AbstractModule]
forall a.
InScope a =>
QName -> [a] -> ScopeInfo -> [AbstractModule]
scopeLookupQualifier QName
x [a]
ys (ScopeInfo -> [AbstractModule])
-> TCMT IO ScopeInfo -> TCMT IO [AbstractModule]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TCMT IO ScopeInfo
forall (m :: * -> *). ReadTCState m => m ScopeInfo
getScope
    modifyTCLens stModuleLookups (ms ++)

-- | Is the 'UnusedImports' warning on?
--   It is sufficient to check for 'UnusedImports_' since it is implied by 'UnusedImportsAll_'.
unusedImportsEnabled :: ScopeM Bool
unusedImportsEnabled :: TCMT IO Bool
unusedImportsEnabled = (WarningName
UnusedImports_ WarningName -> Set WarningName -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member`) (Set WarningName -> Bool)
-> TCMT IO (Set WarningName) -> TCMT IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Lens' TCState (Set WarningName) -> TCMT IO (Set WarningName)
forall (m :: * -> *) a. ReadTCState m => Lens' TCState a -> m a
useTC (Set WarningName -> f (Set WarningName)) -> TCState -> f TCState
Lens' TCState (Set WarningName)
stWarningSet

rangeToPosPos :: HasRange a => a -> Maybe Int
rangeToPosPos :: forall a. HasRange a => a -> Maybe Int
rangeToPosPos = (Position' () -> Int) -> Maybe (Position' ()) -> Maybe Int
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Word32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32 -> Int) -> (Position' () -> Word32) -> Position' () -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Position' () -> Word32
forall a. Position' a -> Word32
P.posPos) (Maybe (Position' ()) -> Maybe Int)
-> (a -> Maybe (Position' ())) -> a -> Maybe Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Range' SrcFile -> Maybe (Position' ())
forall a. Range' a -> Maybe (Position' ())
P.rStart' (Range' SrcFile -> Maybe (Position' ()))
-> (a -> Range' SrcFile) -> a -> Maybe (Position' ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Range' SrcFile
forall a. HasRange a => a -> Range' SrcFile
getRange

-- | Call this when opening a module with all the names it brings into scope.
--   When the 'UnusedImports' warning is enabled, we will store this information
--   to later issue a warning connected to this 'open' statement
--   for the names that were not used.
registerModuleOpening ::
     KwRange             -- ^ Range of the @open@ keyword.
  -> A.ModuleName        -- ^ Parent module: module into which we pour the opened module.
  -> C.QName             -- ^ Opened module.
  -> C.ImportDirective   -- ^ Directive restricting the scope of the opened module.
  -> Scope               -- ^ The scope resulting from applying the import directive.
  -> ScopeM ()
registerModuleOpening :: KwRange
-> ModuleName -> QName -> ImportDirective -> Scope -> ScopeM ()
registerModuleOpening KwRange
kwr ModuleName
currentModule QName
x ImportDirective
dir (Scope ModuleName
m0 [ModuleName]
_parents ScopeNameSpaces
ns Map QName ModuleName
imports Maybe DataOrRecordModule
_dataOrRec) = do
  -- @imports@ have been removed by 'restrictPrivate'.
  Bool -> ScopeM () -> ScopeM ()
forall b (m :: * -> *). (IsBool b, Monad m) => b -> m () -> m ()
unless (Map QName ModuleName -> Bool
forall a. Null a => a -> Bool
null Map QName ModuleName
imports) ScopeM ()
forall a. HasCallStack => a
__IMPOSSIBLE__

  -- When the UnusedImports warning is off, do not collect information about @open@.
  -- E.g. we do not want to see warnings for the automatically inserted
  -- @open import Agda.Primitive using (Set)@.
  doWarn <- TCMT IO Bool
unusedImportsEnabled
  reportSLn "warning.unusedImports" 20 $ unlines
    [ "openedModule: " <> prettyShow doWarn
    , "x = " <> prettyShow x
    -- , "currentModule: " <> prettyShow curM
    ]
  when doWarn $ whenNothing (publicOpen dir) do
    let
      m = Range' SrcFile -> ModuleName -> ModuleName
forall a. SetRange a => Range' SrcFile -> a -> a
setRange (QName -> Range' SrcFile
forall a. HasRange a => a -> Range' SrcFile
getRange QName
x) ModuleName
m0
      names   :: NamesInScope   -- Map C.Name (List1 AbstractName)
      names   = [NamesInScope] -> NamesInScope
forall a. Eq a => [ThingsInScope a] -> ThingsInScope a
mergeNamesMany ([NamesInScope] -> NamesInScope) -> [NamesInScope] -> NamesInScope
forall a b. (a -> b) -> a -> b
$ ((NameSpaceId, NameSpace) -> NamesInScope)
-> ScopeNameSpaces -> [NamesInScope]
forall a b. (a -> b) -> [a] -> [b]
map (NameSpace -> NamesInScope
nsNames (NameSpace -> NamesInScope)
-> ((NameSpaceId, NameSpace) -> NameSpace)
-> (NameSpaceId, NameSpace)
-> NamesInScope
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NameSpaceId, NameSpace) -> NameSpace
forall a b. (a, b) -> b
snd) ScopeNameSpaces
ns
      modules :: ModulesInScope -- Map C.Name (List1 AbstractModule)
      modules = [ModulesInScope] -> ModulesInScope
forall a. Eq a => [ThingsInScope a] -> ThingsInScope a
mergeNamesMany ([ModulesInScope] -> ModulesInScope)
-> [ModulesInScope] -> ModulesInScope
forall a b. (a -> b) -> a -> b
$ ((NameSpaceId, NameSpace) -> ModulesInScope)
-> ScopeNameSpaces -> [ModulesInScope]
forall a b. (a -> b) -> [a] -> [b]
map (NameSpace -> ModulesInScope
nsModules (NameSpace -> ModulesInScope)
-> ((NameSpaceId, NameSpace) -> NameSpace)
-> (NameSpaceId, NameSpace)
-> ModulesInScope
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NameSpaceId, NameSpace) -> NameSpace
forall a b. (a, b) -> b
snd) ScopeNameSpaces
ns
      -- The modules mentioned in the directive.
      explicitModules = [Name] -> Set Name
forall a. Ord a => [a] -> Set a
Set.fromList
        [ Name
y | ImportedModule Name
y <- [ImportedName' Name Name]
usingList [ImportedName' Name Name]
-> [ImportedName' Name Name] -> [ImportedName' Name Name]
forall a. [a] -> [a] -> [a]
++ (Renaming' Name Name -> ImportedName' Name Name)
-> [Renaming' Name Name] -> [ImportedName' Name Name]
forall a b. (a -> b) -> [a] -> [b]
map Renaming' Name Name -> ImportedName' Name Name
forall n m. Renaming' n m -> ImportedName' n m
renTo (ImportDirective -> [Renaming' Name Name]
forall n m. ImportDirective' n m -> RenamingDirective' n m
impRenaming ImportDirective
dir) ]
      usingList = case ImportDirective -> Using' Name Name
forall n m. ImportDirective' n m -> Using' n m
using ImportDirective
dir of
        Using' Name Name
UseEverything -> []
        Using [ImportedName' Name Name]
ys      -> [ImportedName' Name Name]
ys
      -- Modules that come with a name, e.g. modules of data and record types.
      -- We recognize them either by their abstract name,
      -- or by their concrete name (needed for copies made by module application,
      -- since these get fresh abstract names).
      companions = ModulesInScope -> Set Name
forall k a. Map k a -> Set k
Map.keysSet ((Name -> NonEmpty AbstractModule -> Bool)
-> ModulesInScope -> ModulesInScope
forall k a. (k -> a -> Bool) -> Map k a -> Map k a
Map.filterWithKey Name -> NonEmpty AbstractModule -> Bool
isCompanion ModulesInScope
modules)
      isCompanion Name
y NonEmpty AbstractModule
zs = Name -> Set Name -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.notMember Name
y Set Name
explicitModules Bool -> Bool -> Bool
&&
        (Name -> NamesInScope -> Bool
forall k a. Ord k => k -> Map k a -> Bool
Map.member Name
y NamesInScope
names Bool -> Bool -> Bool
forall a. Boolean a => a -> a -> a
|| (AbstractModule -> Bool) -> NonEmpty AbstractModule -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((ModuleName -> Set ModuleName -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` Set ModuleName
nameModules) (ModuleName -> Bool)
-> (AbstractModule -> ModuleName) -> AbstractModule -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AbstractModule -> ModuleName
amodName) NonEmpty AbstractModule
zs)
      nameModules = [ModuleName] -> Set ModuleName
forall a. Ord a => [a] -> Set a
Set.fromList ([ModuleName] -> Set ModuleName) -> [ModuleName] -> Set ModuleName
forall a b. (a -> b) -> a -> b
$ (AbstractName -> ModuleName) -> [AbstractName] -> [ModuleName]
forall a b. (a -> b) -> [a] -> [b]
map (QName -> ModuleName
A.qnameToMName (QName -> ModuleName)
-> (AbstractName -> QName) -> AbstractName -> ModuleName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AbstractName -> QName
anameName) ([AbstractName] -> [ModuleName]) -> [AbstractName] -> [ModuleName]
forall a b. (a -> b) -> a -> b
$ (NonEmpty AbstractName -> [AbstractName])
-> [NonEmpty AbstractName] -> [AbstractName]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap NonEmpty AbstractName -> [Item (NonEmpty AbstractName)]
NonEmpty AbstractName -> [AbstractName]
forall l. IsList l => l -> [Item l]
List1.toList ([NonEmpty AbstractName] -> [AbstractName])
-> [NonEmpty AbstractName] -> [AbstractName]
forall a b. (a -> b) -> a -> b
$ NamesInScope -> [NonEmpty AbstractName]
forall k a. Map k a -> [a]
Map.elems NamesInScope
names
      !k = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
forall a. HasCallStack => a
__IMPOSSIBLE__ (Maybe Int -> Int) -> Maybe Int -> Int
forall a b. (a -> b) -> a -> b
$ QName -> Maybe Int
forall a. HasRange a => a -> Maybe Int
rangeToPosPos QName
x
      hasDir = Bool -> Bool
not (Using' Name Name -> Bool
forall a. Null a => a -> Bool
null (ImportDirective -> Using' Name Name
forall n m. ImportDirective' n m -> Using' n m
using ImportDirective
dir)) Bool -> Bool -> Bool
forall a. Boolean a => a -> a -> a
|| Bool -> Bool
not ([Renaming' Name Name] -> Bool
forall a. Null a => a -> Bool
null (ImportDirective -> [Renaming' Name Name]
forall n m. ImportDirective' n m -> RenamingDirective' n m
impRenaming ImportDirective
dir))
    modifyTCLens stOpenedModules $
      IntMap.insert k (OpenedModule kwr m currentModule hasDir names modules companions)

-- | Call this when a file has been checked to generate the unused-imports warnings for each opened module.
--   Assumes that all names have been looked up via 'lookedupName'
--   and all modules via 'lookedupModule'.
--   Needs the disambiguation information from the type checker to correctly report ununsed overloaded names.
warnUnusedImports :: TCM ()
warnUnusedImports :: ScopeM ()
warnUnusedImports = do
    warnAll <- (WarningName
UnusedImportsAll_ WarningName -> Set WarningName -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member`) (Set WarningName -> Bool)
-> TCMT IO (Set WarningName) -> TCMT IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Lens' TCState (Set WarningName) -> TCMT IO (Set WarningName)
forall (m :: * -> *) a. ReadTCState m => Lens' TCState a -> m a
useTC (Set WarningName -> f (Set WarningName)) -> TCState -> f TCState
Lens' TCState (Set WarningName)
stWarningSet
    st <- useTC stUnusedImportsState
    disambiguatedNames <- useTC stDisambiguatedNames
    -- If instances can be used qualified, they do not need to be imported,
    -- so we should warn about them.
    qualifiedInstances <- optQualifiedInstances <$> pragmaOptions

    reportSLn "warning.unusedImports" 60 $ "ambiguousLookups: " <> prettyShow (ambiguousLookups st)
    reportSLn "warning.unusedImports" 60 $ "unambiguousLookups: " <> prettyShow (unambiguousLookups st)
    reportSLn "warning.unusedImports" 60 $ "moduleLookups: " <> prettyShow (moduleLookups st)

    let
      -- Disambiguate overloaded lookups.
      addAmbLookup (Int
i :: Int) (List2 AbstractName
ys :: List2 AbstractName) = do
        case Int -> IntMap DisambiguatedName -> Maybe DisambiguatedName
forall a. Int -> IntMap a -> Maybe a
IntMap.lookup Int
i IntMap DisambiguatedName
disambiguatedNames of
          Just (DisambiguatedName NameKind
_k QName
x) -> ((AbstractName -> Bool) -> [AbstractName] -> [AbstractName]
forall a. (a -> Bool) -> [a] -> [a]
filter ((QName
x QName -> QName -> Bool
forall a. Eq a => a -> a -> Bool
==) (QName -> Bool) -> (AbstractName -> QName) -> AbstractName -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AbstractName -> QName
anameName) (List2 AbstractName -> [Item (List2 AbstractName)]
forall l. IsList l => l -> [Item l]
List2.toList List2 AbstractName
ys) [AbstractName] -> [AbstractName] -> [AbstractName]
forall a. [a] -> [a] -> [a]
++)
          Maybe DisambiguatedName
Nothing -> (List2 AbstractName -> [Item (List2 AbstractName)]
forall l. IsList l => l -> [Item l]
List2.toList List2 AbstractName
ys [AbstractName] -> [AbstractName] -> [AbstractName]
forall a. [a] -> [a] -> [a]
++) -- __IMPOSSIBLE__
      allLookups :: [AbstractName]
      allLookups = (Int -> List2 AbstractName -> [AbstractName] -> [AbstractName])
-> [AbstractName] -> IntMap (List2 AbstractName) -> [AbstractName]
forall a b. (Int -> a -> b -> b) -> b -> IntMap a -> b
IntMap.foldrWithKey Int -> List2 AbstractName -> [AbstractName] -> [AbstractName]
addAmbLookup (UnusedImportsState -> [AbstractName]
unambiguousLookups UnusedImportsState
st) (UnusedImportsState -> IntMap (List2 AbstractName)
ambiguousLookups UnusedImportsState
st)

      -- To make a set of the list of looked-up 'AbstractName's,
      -- we need to convert them to 'Imported' lest we
      -- conflate names from different openings.
      -- Same for the 'AbstractModule's.
      lookups :: [Imported AbstractName]
      (unknowns, lookups) = partitionMaybe toImported allLookups
      moduleLookups' :: [Imported AbstractModule]
      moduleLookups' = (AbstractModule -> Maybe (Imported AbstractModule))
-> [AbstractModule] -> [Imported AbstractModule]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe AbstractModule -> Maybe (Imported AbstractModule)
forall a. Lineage a => a -> Maybe (Imported a)
toImported ([AbstractModule] -> [Imported AbstractModule])
-> [AbstractModule] -> [Imported AbstractModule]
forall a b. (a -> b) -> a -> b
$ UnusedImportsState -> [AbstractModule]
moduleLookups UnusedImportsState
st

      isInst, isUsedName :: Imported AbstractName -> Bool
      isInst = Maybe KwRange -> Bool
forall a. Maybe a -> Bool
isJust (Maybe KwRange -> Bool)
-> (Imported AbstractName -> Maybe KwRange)
-> Imported AbstractName
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AbstractName -> Maybe KwRange
forall a. IsInstanceDef a => a -> Maybe KwRange
isInstanceDef (AbstractName -> Maybe KwRange)
-> (Imported AbstractName -> AbstractName)
-> Imported AbstractName
-> Maybe KwRange
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Imported AbstractName -> AbstractName
forall a. Imported a -> a
iThing
      isUsedName = Bool
-> ((Imported AbstractName -> Bool)
    -> Imported AbstractName -> Bool)
-> (Imported AbstractName -> Bool)
-> Imported AbstractName
-> Bool
forall b a. IsBool b => b -> (a -> a) -> a -> a
applyUnless Bool
qualifiedInstances (Imported AbstractName -> Bool
isInst (Imported AbstractName -> Bool)
-> (Imported AbstractName -> Bool) -> Imported AbstractName -> Bool
forall a. Boolean a => a -> a -> a
||) ((Imported AbstractName -> Bool) -> Imported AbstractName -> Bool)
-> (Imported AbstractName -> Bool) -> Imported AbstractName -> Bool
forall a b. (a -> b) -> a -> b
$ [Imported AbstractName] -> Imported AbstractName -> Bool
forall a. Ord a => [a] -> a -> Bool
hasElem [Imported AbstractName]
lookups
      isUsedModule :: Imported AbstractModule -> Bool
      isUsedModule = [Imported AbstractModule] -> Imported AbstractModule -> Bool
forall a. Ord a => [a] -> a -> Bool
hasElem [Imported AbstractModule]
moduleLookups'

    reportSLn "warning.unusedImports" 60 $ "allLookups: " <> prettyShow allLookups
    reportSLn "warning.unusedImports" 60 $ "lookups: " <> prettyShow lookups
    reportSLn "warning.unusedImports" 60 $ "unknowns: " <> prettyShow unknowns

    -- Iterate through the @open@ statements and issue warnings.
    forM_ (openedModules st) \ (OpenedModule KwRange
kwr ModuleName
m ModuleName
parent Bool
hasDir NamesInScope
names ModulesInScope
modules Set Name
companions) -> do

      -- Partition the names and modules brought into scope by the open statement
      -- into used and unused ones.
      (usedNames, unusedNames) <- (Imported AbstractName -> Bool)
-> NamesInScope -> TCMT IO ([Name], [Name])
forall a (m :: * -> *).
(Lineage a, MonadDebug m) =>
(Imported a -> Bool) -> ThingsInScope a -> m ([Name], [Name])
partitionUsed Imported AbstractName -> Bool
isUsedName NamesInScope
names
      (usedModules, unusedModules) <- partitionUsed isUsedModule modules

      let
        used = Bool -> Bool
not ([Name] -> Bool
forall a. Null a => a -> Bool
null [Name]
usedNames Bool -> Bool -> Bool
&& [Name] -> Bool
forall a. Null a => a -> Bool
null [Name]
usedModules)
        -- Unused things to report individually, in alphabetical order.
        -- We omit the modules of data and record types not mentioned explicitly,
        -- since we report their names already.
        unused :: [C.ImportedName]
        unused = (ImportedName' Name Name -> Name)
-> [ImportedName' Name Name] -> [ImportedName' Name Name]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn ImportedName' Name Name -> Name
forall a. ImportedName' a a -> a
fromImportedName ([ImportedName' Name Name] -> [ImportedName' Name Name])
-> [ImportedName' Name Name] -> [ImportedName' Name Name]
forall a b. (a -> b) -> a -> b
$ [[ImportedName' Name Name]] -> [ImportedName' Name Name]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
          [ (Name -> ImportedName' Name Name)
-> [Name] -> [ImportedName' Name Name]
forall a b. (a -> b) -> [a] -> [b]
map Name -> ImportedName' Name Name
forall n m. n -> ImportedName' n m
ImportedName [Name]
unusedNames
          , (Name -> ImportedName' Name Name)
-> [Name] -> [ImportedName' Name Name]
forall a b. (a -> b) -> [a] -> [b]
map Name -> ImportedName' Name Name
forall n m. m -> ImportedName' n m
ImportedModule ([Name] -> [ImportedName' Name Name])
-> [Name] -> [ImportedName' Name Name]
forall a b. (a -> b) -> a -> b
$ (Name -> Bool) -> [Name] -> [Name]
forall a. (a -> Bool) -> [a] -> [a]
filter (Name -> Set Name -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.notMember` Set Name
companions) [Name]
unusedModules
          ]

        -- Commands to issue the warnings:
        warn = Range' SrcFile -> ScopeM () -> ScopeM ()
forall (m :: * -> *) x a.
(MonadTrace m, HasRange x) =>
x -> m a -> m a
setCurrentRange ((KwRange, ModuleName) -> Range' SrcFile
forall a. HasRange a => a -> Range' SrcFile
getRange (KwRange
kwr, ModuleName
m)) (ScopeM () -> ScopeM ())
-> (Maybe (List1 (ImportedName' Name Name)) -> ScopeM ())
-> Maybe (List1 (ImportedName' Name Name))
-> ScopeM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ModuleName -> ScopeM () -> ScopeM ()
forall (m :: * -> *) a.
(ReadTCState m, MonadTCState m) =>
ModuleName -> m a -> m a
withCurrentModule ModuleName
parent (ScopeM () -> ScopeM ())
-> (Maybe (List1 (ImportedName' Name Name)) -> ScopeM ())
-> Maybe (List1 (ImportedName' Name Name))
-> ScopeM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Warning -> ScopeM ()
forall (m :: * -> *).
(HasCallStack, MonadWarning m) =>
Warning -> m ()
warning (Warning -> ScopeM ())
-> (Maybe (List1 (ImportedName' Name Name)) -> Warning)
-> Maybe (List1 (ImportedName' Name Name))
-> ScopeM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ModuleName -> Maybe (List1 (ImportedName' Name Name)) -> Warning
UnusedImports ModuleName
m
        warnModule = Maybe (List1 (ImportedName' Name Name)) -> ScopeM ()
warn Maybe (List1 (ImportedName' Name Name))
forall a. Maybe a
Nothing
        warnEach = [ImportedName' Name Name]
-> (List1 (ImportedName' Name Name) -> ScopeM ()) -> ScopeM ()
forall (m :: * -> *) a.
Applicative m =>
[a] -> (List1 a -> m ()) -> m ()
List1.unlessNull [ImportedName' Name Name]
unused ((List1 (ImportedName' Name Name) -> ScopeM ()) -> ScopeM ())
-> (List1 (ImportedName' Name Name) -> ScopeM ()) -> ScopeM ()
forall a b. (a -> b) -> a -> b
$ Maybe (List1 (ImportedName' Name Name)) -> ScopeM ()
warn (Maybe (List1 (ImportedName' Name Name)) -> ScopeM ())
-> (List1 (ImportedName' Name Name)
    -> Maybe (List1 (ImportedName' Name Name)))
-> List1 (ImportedName' Name Name)
-> ScopeM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. List1 (ImportedName' Name Name)
-> Maybe (List1 (ImportedName' Name Name))
forall a. a -> Maybe a
Just

      -- Issue warning.
      -- If nothing was used, we warn about the whole import.
      -- If the open statement has a 'using' or 'renaming' directive,
      -- or if the 'UnusedImportsAll_' warning is enabled,
      -- we warn about each unused name individually.
      -- Otherwise, we just warn once about the whole import.
      if  | hasDir      -> warnEach
          | not used    -> warnModule
          | warnAll     -> warnEach
          | otherwise   -> pure ()

-- | Partition the things brought into scope by an @open@ statement
--   into the used and unused ones.
--   Returns the concrete names they are in scope under.
partitionUsed :: forall a m. (Lineage a, MonadDebug m)
  => (Imported a -> Bool)  -- ^ Was the thing used?
  -> ThingsInScope a       -- ^ The things brought into scope by the @open@.
  -> m ([C.Name], [C.Name])
partitionUsed :: forall a (m :: * -> *).
(Lineage a, MonadDebug m) =>
(Imported a -> Bool) -> ThingsInScope a -> m ([Name], [Name])
partitionUsed Imported a -> Bool
isUsed ThingsInScope a
sc = do
  let
    imps, used, unused :: [(C.Name, List1 (Imported a))]
    ([(Name, NonEmpty a)]
other, [(Name, List1 (Imported a))]
imps) = ((Name, NonEmpty a) -> Maybe (Name, List1 (Imported a)))
-> [(Name, NonEmpty a)]
-> ([(Name, NonEmpty a)], [(Name, List1 (Imported a))])
forall a b. (a -> Maybe b) -> [a] -> ([a], [b])
partitionMaybe ((NonEmpty a -> Maybe (List1 (Imported a)))
-> (Name, NonEmpty a) -> Maybe (Name, List1 (Imported a))
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> (Name, a) -> f (Name, b)
traverse ((NonEmpty a -> Maybe (List1 (Imported a)))
 -> (Name, NonEmpty a) -> Maybe (Name, List1 (Imported a)))
-> (NonEmpty a -> Maybe (List1 (Imported a)))
-> (Name, NonEmpty a)
-> Maybe (Name, List1 (Imported a))
forall a b. (a -> b) -> a -> b
$ (a -> Maybe (Imported a))
-> NonEmpty a -> Maybe (List1 (Imported a))
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> NonEmpty a -> f (NonEmpty b)
traverse a -> Maybe (Imported a)
forall a. Lineage a => a -> Maybe (Imported a)
toImported) ([(Name, NonEmpty a)]
 -> ([(Name, NonEmpty a)], [(Name, List1 (Imported a))]))
-> [(Name, NonEmpty a)]
-> ([(Name, NonEmpty a)], [(Name, List1 (Imported a))])
forall a b. (a -> b) -> a -> b
$ ThingsInScope a -> [(Name, NonEmpty a)]
forall k a. Map k a -> [(k, a)]
Map.toList ThingsInScope a
sc
    ([(Name, List1 (Imported a))]
used, [(Name, List1 (Imported a))]
unused) = ((Name, List1 (Imported a)) -> Bool)
-> [(Name, List1 (Imported a))]
-> ([(Name, List1 (Imported a))], [(Name, List1 (Imported a))])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition ((Imported a -> Bool) -> List1 (Imported a) -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Imported a -> Bool
isUsed (List1 (Imported a) -> Bool)
-> ((Name, List1 (Imported a)) -> List1 (Imported a))
-> (Name, List1 (Imported a))
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Name, List1 (Imported a)) -> List1 (Imported a)
forall a b. (a, b) -> b
snd) [(Name, List1 (Imported a))]
imps
  String -> Int -> String -> m ()
forall (m :: * -> *).
MonadDebug m =>
String -> Int -> String -> m ()
reportSLn String
"warning.unusedImports" Int
60 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ String
"used: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [(Name, List1 (Imported a))] -> String
forall a. Pretty a => a -> String
prettyShow [(Name, List1 (Imported a))]
used
  String -> Int -> String -> m ()
forall (m :: * -> *).
MonadDebug m =>
String -> Int -> String -> m ()
reportSLn String
"warning.unusedImports" Int
60 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ String
"unused: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [(Name, List1 (Imported a))] -> String
forall a. Pretty a => a -> String
prettyShow [(Name, List1 (Imported a))]
unused
  Bool -> m () -> m ()
forall b (m :: * -> *). (IsBool b, Monad m) => b -> m () -> m ()
unless ([(Name, NonEmpty a)] -> Bool
forall a. Null a => a -> Bool
null [(Name, NonEmpty a)]
other) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ String -> m ()
forall (m :: * -> *) a.
(HasCallStack, MonadDebug m) =>
String -> m a
__IMPOSSIBLE_VERBOSE__ ([(Name, NonEmpty a)] -> String
forall a. Show a => a -> String
show [(Name, NonEmpty a)]
other)
  ([Name], [Name]) -> m ([Name], [Name])
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (((Name, List1 (Imported a)) -> Name)
-> [(Name, List1 (Imported a))] -> [Name]
forall a b. (a -> b) -> [a] -> [b]
map (Name, List1 (Imported a)) -> Name
forall a b. (a, b) -> a
fst [(Name, List1 (Imported a))]
used, ((Name, List1 (Imported a)) -> Name)
-> [(Name, List1 (Imported a))] -> [Name]
forall a b. (a -> b) -> [a] -> [b]
map (Name, List1 (Imported a)) -> Name
forall a b. (a, b) -> a
fst [(Name, List1 (Imported a))]
unused)

------------------------------------------------------------------------------
-- * Auxiliary definitions

-- | Things (names and modules) that remember how they came into scope.
class (Ord a, Show a, Pretty a) => Lineage a where
  lineage :: a -> WhyInScope

instance Lineage AbstractName where
  lineage :: AbstractName -> WhyInScope
lineage = AbstractName -> WhyInScope
anameLineage

instance Lineage AbstractModule where
  lineage :: AbstractModule -> WhyInScope
lineage = AbstractModule -> WhyInScope
amodLineage

-- | A wrapper around 'AbstractName' or 'AbstractModule'
--   to make the position of the 'Opened' in the lineage available.
--   This wrapper is needed when 'AbstractName's are stored in sets
--   so that we do not conflate different 'AbstractName's with the same underlying 'A.QName'
--   that were brought into scope by different 'open' statements.
data Imported a = Imported
  { forall a. Imported a -> Int
iWhere :: Int -- Position of 'Opened' extracted from the 'AbstractName'.
  , forall a. Imported a -> a
iThing :: a
  } deriving (Imported a -> Imported a -> Bool
(Imported a -> Imported a -> Bool)
-> (Imported a -> Imported a -> Bool) -> Eq (Imported a)
forall a. Eq a => Imported a -> Imported a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. Eq a => Imported a -> Imported a -> Bool
== :: Imported a -> Imported a -> Bool
$c/= :: forall a. Eq a => Imported a -> Imported a -> Bool
/= :: Imported a -> Imported a -> Bool
Eq, Eq (Imported a)
Eq (Imported a) =>
(Imported a -> Imported a -> Ordering)
-> (Imported a -> Imported a -> Bool)
-> (Imported a -> Imported a -> Bool)
-> (Imported a -> Imported a -> Bool)
-> (Imported a -> Imported a -> Bool)
-> (Imported a -> Imported a -> Imported a)
-> (Imported a -> Imported a -> Imported a)
-> Ord (Imported a)
Imported a -> Imported a -> Bool
Imported a -> Imported a -> Ordering
Imported a -> Imported a -> Imported a
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall a. Ord a => Eq (Imported a)
forall a. Ord a => Imported a -> Imported a -> Bool
forall a. Ord a => Imported a -> Imported a -> Ordering
forall a. Ord a => Imported a -> Imported a -> Imported a
$ccompare :: forall a. Ord a => Imported a -> Imported a -> Ordering
compare :: Imported a -> Imported a -> Ordering
$c< :: forall a. Ord a => Imported a -> Imported a -> Bool
< :: Imported a -> Imported a -> Bool
$c<= :: forall a. Ord a => Imported a -> Imported a -> Bool
<= :: Imported a -> Imported a -> Bool
$c> :: forall a. Ord a => Imported a -> Imported a -> Bool
> :: Imported a -> Imported a -> Bool
$c>= :: forall a. Ord a => Imported a -> Imported a -> Bool
>= :: Imported a -> Imported a -> Bool
$cmax :: forall a. Ord a => Imported a -> Imported a -> Imported a
max :: Imported a -> Imported a -> Imported a
$cmin :: forall a. Ord a => Imported a -> Imported a -> Imported a
min :: Imported a -> Imported a -> Imported a
Ord, Int -> Imported a -> String -> String
[Imported a] -> String -> String
Imported a -> String
(Int -> Imported a -> String -> String)
-> (Imported a -> String)
-> ([Imported a] -> String -> String)
-> Show (Imported a)
forall a. Show a => Int -> Imported a -> String -> String
forall a. Show a => [Imported a] -> String -> String
forall a. Show a => Imported a -> String
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: forall a. Show a => Int -> Imported a -> String -> String
showsPrec :: Int -> Imported a -> String -> String
$cshow :: forall a. Show a => Imported a -> String
show :: Imported a -> String
$cshowList :: forall a. Show a => [Imported a] -> String -> String
showList :: [Imported a] -> String -> String
Show)

instance Pretty a => Pretty (Imported a) where
  pretty :: Imported a -> Doc
pretty (Imported Int
i a
n) = a -> Doc
forall a. Pretty a => a -> Doc
pretty a
n Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
" (at position " Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Int -> Doc
forall a. Pretty a => a -> Doc
pretty Int
i Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
")"

-- | Convert an 'AbstractName' or 'AbstractModule' to an 'Imported'
--   if it was brought into scope by an 'open' statement.
toImported :: Lineage a => a -> Maybe (Imported a)
toImported :: forall a. Lineage a => a -> Maybe (Imported a)
toImported a
x = case a -> WhyInScope
forall a. Lineage a => a -> WhyInScope
lineage a
x of
  Opened QName
m WhyInScope
_ -> QName -> Maybe Int
forall a. HasRange a => a -> Maybe Int
rangeToPosPos QName
m Maybe Int -> (Int -> Imported a) -> Maybe (Imported a)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> (Int -> a -> Imported a
forall a. Int -> a -> Imported a
`Imported` a
x)
  Applied{} -> Maybe (Imported a)
forall a. Maybe a
Nothing
  Defined{} -> Maybe (Imported a)
forall a. Maybe a
Nothing

-- Lenses for the components of the UnusedImportsState in the TCState.

stUnambiguousLookups :: Lens' TCState [AbstractName]
stUnambiguousLookups :: Lens' TCState [AbstractName]
stUnambiguousLookups = (UnusedImportsState -> f UnusedImportsState)
-> TCState -> f TCState
Lens' TCState UnusedImportsState
stUnusedImportsState ((UnusedImportsState -> f UnusedImportsState)
 -> TCState -> f TCState)
-> (([AbstractName] -> f [AbstractName])
    -> UnusedImportsState -> f UnusedImportsState)
-> ([AbstractName] -> f [AbstractName])
-> TCState
-> f TCState
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([AbstractName] -> f [AbstractName])
-> UnusedImportsState -> f UnusedImportsState
Lens' UnusedImportsState [AbstractName]
lensUnambiguousLookups

stAmbiguousLookups :: Lens' TCState (IntMap (List2 AbstractName))
stAmbiguousLookups :: Lens' TCState (IntMap (List2 AbstractName))
stAmbiguousLookups = (UnusedImportsState -> f UnusedImportsState)
-> TCState -> f TCState
Lens' TCState UnusedImportsState
stUnusedImportsState ((UnusedImportsState -> f UnusedImportsState)
 -> TCState -> f TCState)
-> ((IntMap (List2 AbstractName)
     -> f (IntMap (List2 AbstractName)))
    -> UnusedImportsState -> f UnusedImportsState)
-> (IntMap (List2 AbstractName) -> f (IntMap (List2 AbstractName)))
-> TCState
-> f TCState
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (IntMap (List2 AbstractName) -> f (IntMap (List2 AbstractName)))
-> UnusedImportsState -> f UnusedImportsState
Lens' UnusedImportsState (IntMap (List2 AbstractName))
lensAmbiguousLookups

stModuleLookups :: Lens' TCState [AbstractModule]
stModuleLookups :: Lens' TCState [AbstractModule]
stModuleLookups = (UnusedImportsState -> f UnusedImportsState)
-> TCState -> f TCState
Lens' TCState UnusedImportsState
stUnusedImportsState ((UnusedImportsState -> f UnusedImportsState)
 -> TCState -> f TCState)
-> (([AbstractModule] -> f [AbstractModule])
    -> UnusedImportsState -> f UnusedImportsState)
-> ([AbstractModule] -> f [AbstractModule])
-> TCState
-> f TCState
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([AbstractModule] -> f [AbstractModule])
-> UnusedImportsState -> f UnusedImportsState
Lens' UnusedImportsState [AbstractModule]
lensModuleLookups

stOpenedModules :: Lens' TCState (IntMap OpenedModule)
stOpenedModules :: Lens' TCState (IntMap OpenedModule)
stOpenedModules = (UnusedImportsState -> f UnusedImportsState)
-> TCState -> f TCState
Lens' TCState UnusedImportsState
stUnusedImportsState ((UnusedImportsState -> f UnusedImportsState)
 -> TCState -> f TCState)
-> ((IntMap OpenedModule -> f (IntMap OpenedModule))
    -> UnusedImportsState -> f UnusedImportsState)
-> (IntMap OpenedModule -> f (IntMap OpenedModule))
-> TCState
-> f TCState
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (IntMap OpenedModule -> f (IntMap OpenedModule))
-> UnusedImportsState -> f UnusedImportsState
Lens' UnusedImportsState (IntMap OpenedModule)
lensOpenedModules