diff options
-rw-r--r-- | compiler/GHC/ByteCode/Types.hs | 10 | ||||
-rw-r--r-- | docs/users_guide/8.12.1-notes.rst | 5 | ||||
-rw-r--r-- | docs/users_guide/ghci.rst | 46 | ||||
-rw-r--r-- | ghc/GHCi/UI.hs | 300 | ||||
-rw-r--r-- | testsuite/tests/ghci.debugger/scripts/T17989.stdout | 17 | ||||
-rw-r--r-- | testsuite/tests/ghci.debugger/scripts/T3000.hs | 4 | ||||
-rw-r--r-- | testsuite/tests/ghci.debugger/scripts/T3000.script | 16 | ||||
-rw-r--r-- | testsuite/tests/ghci.debugger/scripts/T3000.stdout | 21 | ||||
-rw-r--r-- | testsuite/tests/ghci.debugger/scripts/T3000S.hs | 13 | ||||
-rw-r--r-- | testsuite/tests/ghci.debugger/scripts/all.T | 1 | ||||
-rw-r--r-- | testsuite/tests/ghci.debugger/scripts/break002.stdout | 4 | ||||
-rw-r--r-- | testsuite/tests/ghci.debugger/scripts/break019.stderr | 2 | ||||
-rw-r--r-- | testsuite/tests/ghci.debugger/scripts/break019.stdout | 1 |
13 files changed, 337 insertions, 103 deletions
diff --git a/compiler/GHC/ByteCode/Types.hs b/compiler/GHC/ByteCode/Types.hs index 55ad604447..296f886a29 100644 --- a/compiler/GHC/ByteCode/Types.hs +++ b/compiler/GHC/ByteCode/Types.hs @@ -154,6 +154,7 @@ data ModBreaks -- ^ An array giving the names of the free variables at each breakpoint. , modBreaks_decls :: !(Array BreakIndex [String]) -- ^ An array giving the names of the declarations enclosing each breakpoint. + -- See Note [Field modBreaks_decls] , modBreaks_ccs :: !(Array BreakIndex (RemotePtr CostCentre)) -- ^ Array pointing to cost centre for each breakpoint , modBreaks_breakInfo :: IntMap CgBreakInfo @@ -180,3 +181,12 @@ emptyModBreaks = ModBreaks , modBreaks_ccs = array (0,-1) [] , modBreaks_breakInfo = IntMap.empty } + +{- +Note [Field modBreaks_decls] +~~~~~~~~~~~~~~~~~~~~~~ +A value of eg ["foo", "bar", "baz"] in a `modBreaks_decls` field means: +The breakpoint is in the function called "baz" that is declared in a `let` +or `where` clause of a declaration called "bar", which itself is declared +in a `let` or `where` clause of the top-level function called "foo". +-} diff --git a/docs/users_guide/8.12.1-notes.rst b/docs/users_guide/8.12.1-notes.rst index bc2414489f..1bf9464db4 100644 --- a/docs/users_guide/8.12.1-notes.rst +++ b/docs/users_guide/8.12.1-notes.rst @@ -161,6 +161,11 @@ GHCi passed as arguments: either by enclosing the file names in double quotes or by escaping spaces in file names with a backslash. (:ghc-ticket:`18027`) +- The GHCi debugger syntax ``:break <qualified.name>`` now allows to set + breakpoints on all functions. The restrictions ``top-Level`` and ``exported`` + have been removed. Hence it's now possible to use this syntax to set + breakpoints on functions defined in nested ``where`` or ``let`` clauses. + Runtime system ~~~~~~~~~~~~~~ diff --git a/docs/users_guide/ghci.rst b/docs/users_guide/ghci.rst index 390719ff80..9f63e0c50b 100644 --- a/docs/users_guide/ghci.rst +++ b/docs/users_guide/ghci.rst @@ -1520,12 +1520,52 @@ breakpoint is to name a top-level function: .. code-block:: none - :break identifier + :break identifier Where ⟨identifier⟩ names any top-level function in an interpreted module currently loaded into GHCi (qualified names may be used). The breakpoint -will be set on the body of the function, when it is fully applied but -before any pattern matching has taken place. +will be set on the body of the function, when it is fully applied. +If the function has several patterns, then a breakpoint will be set on +each of them. + +By using qualified names, one can set breakpoints on all functions +(top-level and nested) in every loaded and interpreted module: + +.. code-block:: none + + :break [ModQual.]topLevelIdent[.nestedIdent]...[.nestedIdent] + +⟨ModQual⟩ is optional and is either the effective name of a module or +the local alias of a qualified import statement. + +⟨topLevelIdent⟩ is the name of a top level function in the module +referenced by ⟨ModQual⟩. + +⟨nestedIdent⟩ is optional and the name of a function nested in a let or +where clause inside the previously mentioned function ⟨nestedIdent⟩ or +⟨topLevelIdent⟩. + +If ⟨ModQual⟩ is a module name, then ⟨topLevelIdent⟩ can be any top level +identifier in this module. If ⟨ModQual⟩ is missing or a local alias of a +qualified import, then ⟨topLevelIdent⟩ must be in scope. + +Breakpoints can be set on arbitrarily deeply nested functions, but the +whole chain of nested function names must be specified. + +Consider the function ``foo`` in a module ``Main``: + +.. code-block:: none + + foo s = 'a' : add s + where add = (++"z") + +The breakpoint on the function ``add`` can be set with one of the +following commands: + +.. code-block:: none + + :break Main.foo.add + :break foo.add Breakpoints can also be set by line (and optionally column) number: diff --git a/ghc/GHCi/UI.hs b/ghc/GHCi/UI.hs index 9db2dd5773..5f6bea091a 100644 --- a/ghc/GHCi/UI.hs +++ b/ghc/GHCi/UI.hs @@ -54,7 +54,7 @@ import GHC.Hs.ImpExp import GHC.Hs import GHC.Driver.Types ( tyThingParent_maybe, handleFlagWarnings, getSafeMode, hsc_IC, setInteractivePrintName, hsc_dflags, msObjFilePath, runInteractiveHsc, - hsc_dynLinker, hsc_interp ) + hsc_dynLinker, hsc_interp, emptyModBreaks ) import GHC.Unit.Module import GHC.Types.Name import GHC.Unit.State ( unitIsTrusted, unsafeLookupUnit, unsafeLookupUnitId, @@ -3380,67 +3380,74 @@ completeIdentifier line@(left, _) = dflags <- GHC.getSessionDynFlags return (filter (w `isPrefixOf`) (map (showPpr dflags) rdrs)) - -completeBreakpoint = wrapCompleter spaces $ \w -> do -- #17989 - -- See Note [Tab-completion for :break] - -- Pif ~ Pair with Identifier name and File name - pifsBreaks <- pifsFromModBreaks - pifsInscope <- pifsInscopeByPrefix w - pure $ [n | (n,f) <- pifsInscope, (unQual n, f) `elem` pifsBreaks] +-- TAB-completion for the :break command. +-- Build and return a list of breakpoint identifiers with a given prefix. +-- See Note [Tab-completion for :break] +completeBreakpoint = wrapCompleter spaces $ \w -> do -- #3000 + -- bid ~ breakpoint identifier = a name of a function that is + -- eligible to set a breakpoint. + let (mod_str, _, _) = splitIdent w + bids_mod_breaks <- bidsFromModBreaks mod_str + bids_inscopes <- bidsFromInscopes + pure $ nub $ filter (isPrefixOf w) $ bids_mod_breaks ++ bids_inscopes where - -- Extract from the ModBreaks data all the names of top-level - -- functions eligible to set breakpoints, and put them - -- into a pair together with the filename where they are defined. - pifsFromModBreaks :: GhciMonad m => m [(String, FastString)] - pifsFromModBreaks = do + -- Extract all bids from ModBreaks for a given module name prefix + bidsFromModBreaks :: GhciMonad m => String -> m [String] + bidsFromModBreaks mod_pref = do + imods <- interpretedHomeMods + let pmods = filter ((isPrefixOf mod_pref) . showModule) imods + nonquals <- case null mod_pref of + -- If the prefix is empty, then for functions declared in a module + -- in scope, don't qualify the function name. + -- (eg: `main` instead of `Main.main`) + True -> do + imports <- GHC.getContext + pure [ m | IIModule m <- imports] + False -> return [] + bidss <- mapM (bidsByModule nonquals) pmods + pure $ concat bidss + + -- Return a list of interpreted home modules + interpretedHomeMods :: GhciMonad m => m [Module] + interpretedHomeMods = do graph <- GHC.getModuleGraph - imods <- filterM GHC.moduleIsInterpreted $ - ms_mod <$> GHC.mgModSummaries graph - topDecls <- mapM pifsFromModBreaksByModule imods - pure $ concat topDecls - - -- Return all possible top-level pifs from the ModBreaks - -- for one module. - -- Identifiers of ModBreaks pifs are never qualified. - pifsFromModBreaksByModule :: GhciMonad m => Module -> m [(String, FastString)] - pifsFromModBreaksByModule mod = do - (_, locs, decls) <- getModBreak mod - let mbFile = safeHead $ mapMaybe srcSpanFileName_maybe $ elems locs - -- The first element in `decls` is the name of the top-level function. - let topLvlDecls = nub $ mapMaybe safeHead $ elems decls - pure $ case mbFile of - Nothing -> [] - (Just file) -> zip topLvlDecls $ repeat file - where - safeHead [] = Nothing - safeHead (h : _) = Just h - - -- Return the pifs of all identifieres (RdrNames) in scope, where - -- the identifier has the given prefix. - -- Identifiers of inscope pifs maybe qualified. - pifsInscopeByPrefix :: GhciMonad m => String -> m [(String, FastString)] - pifsInscopeByPrefix pref = do - dflags <- GHC.getSessionDynFlags + let hmods = ms_mod <$> GHC.mgModSummaries graph + filterM GHC.moduleIsInterpreted hmods + + -- Return all possible bids for a given Module + bidsByModule :: GhciMonad m => [ModuleName] -> Module -> m [String] + bidsByModule nonquals mod = do + (_, _, decls) <- getModBreak mod + let bids = nub $ declPath <$> elems decls + pure $ case (moduleName mod) `elem` nonquals of + True -> bids + False -> (combineModIdent (showModule mod)) <$> bids + + -- Extract all bids from all top-level identifiers in scope. + bidsFromInscopes :: GhciMonad m => m [String] + bidsFromInscopes = do rdrs <- GHC.getRdrNamesInScope - let strnams = (filter (pref `isPrefixOf`) (map (showPpr dflags) rdrs)) - nams_fil <- mapM createInscopePif strnams - pure $ concat nams_fil - - -- Return a list of pifs for a single in scope identifier - createInscopePif :: GhciMonad m => String -> m [(String, FastString)] - createInscopePif str_rdr = do + inscopess <- mapM createInscope $ (showSDocUnsafe . ppr) <$> rdrs + imods <- interpretedHomeMods + let topLevels = filter ((`elem` imods) . snd) $ concat inscopess + bidss <- mapM (addNestedDecls) topLevels + pure $ concat bidss + + -- Return a list of (bid,module) for a single top-level in-scope identifier + createInscope :: GhciMonad m => String -> m [(String, Module)] + createInscope str_rdr = do names <- GHC.parseName str_rdr - let files = mapMaybe srcSpanFileName_maybe $ map nameSrcSpan names - pure $ zip (repeat str_rdr) files - - -- unQual "ModLev.Module.func" -> "func" - unQual :: String -> String - unQual qual_unqual = - let ixs = elemIndices '.' qual_unqual - in case ixs of - [] -> qual_unqual - _ -> drop (1 + last ixs) qual_unqual - + pure $ zip (repeat str_rdr) $ GHC.nameModule <$> names + + -- For every top-level identifier in scope, add the bids of the nested + -- declarations. See Note [ModBreaks.decls] in GHC.ByteCode.Types + addNestedDecls :: GhciMonad m => (String, Module) -> m [String] + addNestedDecls (ident, mod) = do + (_, _, decls) <- getModBreak mod + let (mod_str, topLvl, _) = splitIdent ident + ident_decls = filter ((topLvl ==) . head) $ elems decls + bids = nub $ declPath <$> ident_decls + pure $ map (combineModIdent mod_str) bids completeModule = wrapIdentCompleter $ \w -> do dflags <- GHC.getSessionDynFlags @@ -3523,40 +3530,33 @@ allVisibleModules dflags = listVisibleModuleNames (unitState dflags) completeExpression = completeQuotedWord (Just '\\') "\"" listFiles completeIdentifier + {- Note [Tab-completion for :break] --------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In tab-completion for the `:break` command, only those identifiers should be shown, that are accepted in the `:break` command. Hence these identifiers must be - defined in an interpreted module -- top-level -- currently in scope - listed in a `ModBreaks` value as a possible breakpoint. The identifiers may be qualified or unqualified. -To get all possible top-level breakpoints for tab-completeion +To get all possible top-level breakpoints for tab-completion with the correct qualification do: -1. Build the list called `pifsBreaks` of all pairs of -(Identifier, module-filename) from the `ModBreaks` values. -Here all identifiers are unqualified. - -2. Build the list called `pifInscope` of all pairs of -(Identifiers, module-filename) with identifiers from -the `GlobalRdrEnv`. Take only those identifiers that are -in scope and have the correct prefix. -Here the identifiers may be qualified. +1. Build a list called `bids_mod_breaks` of identifier names eligible +for setting breakpoints: For every interpreted module with the +correct module prefix read all identifier names from the `decls` field +of the `ModBreaks` array. -3. From the `pifInscope` list seclect all pairs that can be -found in the `pifsBreaks` list, by comparing only the -unqualified part of the identifier. -The remaining identifiers can be used for tab-completion. +2. Build a list called `bids_inscopess` of identifiers in scope: +Take all RdrNames in scope, and filter by interpreted modules. +Fore each of these top-level identifiers add from the `ModBreaks` +arrays the available identifiers of the nested functions. -This ensures, that we show only identifiers, that can be used -in a `:break` command. +3.) Combine both lists, filter by the given prefix, and remove duplicates. -} -- ----------------------------------------------------------------------------- @@ -3791,17 +3791,7 @@ breakSwitch (arg1:rest) [] -> do liftIO $ putStrLn "No modules are loaded with debugging support." | otherwise = do -- try parsing it as an identifier - wantNameFromInterpretedModule noCanDo arg1 $ \name -> do - maybe_info <- GHC.getModuleInfo (GHC.nameModule name) - case maybe_info of - Nothing -> noCanDo name (ptext (sLit "cannot get module info")) - Just minf -> - ASSERT( isExternalName name ) - findBreakAndSet (GHC.nameModule name) $ - findBreakForBind name (GHC.modInfoModBreaks minf) - where - noCanDo n why = printForUser $ - text "cannot set breakpoint on " <> ppr n <> text ": " <> why + breakById arg1 breakByModule :: GhciMonad m => Module -> [String] -> m () breakByModule md (arg1:rest) @@ -3817,8 +3807,72 @@ breakByModuleLine md line args findBreakAndSet md $ maybeToList . findBreakByCoord Nothing (line, read col) | otherwise = breakSyntax +-- Set a breakpoint for an identifier +-- See Note [Setting Breakpoints by Id] +breakById :: GhciMonad m => String -> m () -- #3000 +breakById inp = do + let (mod_str, top_level, fun_str) = splitIdent inp + mod_top_lvl = combineModIdent mod_str top_level + mb_mod <- catch (lookupModuleInscope mod_top_lvl) + (\(_ :: SomeException) -> lookupModuleInGraph mod_str) + -- If the top-level name is not in scope, `lookupModuleInscope` will + -- throw an exception, then lookup the module name in the module graph. + mb_err_msg <- validateBP mod_str fun_str mb_mod + case mb_err_msg of + Just err_msg -> printForUser $ + text "Cannot set breakpoint on" <+> quotes (text inp) + <> text ":" <+> err_msg + Nothing -> do + -- No errors found, go and set the breakpoint + mb_mod_info <- GHC.getModuleInfo $ fromJust mb_mod + let modBreaks = case mb_mod_info of + (Just mod_info) -> GHC.modInfoModBreaks mod_info + Nothing -> emptyModBreaks + findBreakAndSet (fromJust mb_mod) $ findBreakForBind fun_str modBreaks + where + -- Try to lookup the module for an identifier that is in scope. + -- `parseName` throws an exception, if the identifier is not in scope + lookupModuleInscope :: GhciMonad m => String -> m (Maybe Module) + lookupModuleInscope mod_top_lvl = do + names <- GHC.parseName mod_top_lvl + pure $ Just $ head $ GHC.nameModule <$> names + -- if GHC.parseName succeeds `names` is not empty! + -- if it fails, the last line will not be evaluated. + + -- Lookup the Module of a module name in the module graph + lookupModuleInGraph :: GhciMonad m => String -> m (Maybe Module) + lookupModuleInGraph mod_str = do + graph <- GHC.getModuleGraph + let hmods = ms_mod <$> GHC.mgModSummaries graph + pure $ find ((== mod_str) . showModule) hmods + + -- Check validity of an identifier to set a breakpoint: + -- 1. The module of the identifier must exist + -- 2. the identifier must be in an interpreted module + -- 3. the ModBreaks array for module `mod` must have an entry + -- for the function + validateBP :: GhciMonad m => String -> String -> Maybe Module + -> m (Maybe SDoc) + validateBP mod_str fun_str Nothing = pure $ Just $ quotes (text + (combineModIdent mod_str (Prelude.takeWhile (/= '.') fun_str))) + <+> text "not in scope" + validateBP _ "" (Just _) = pure $ Just $ text "Function name is missing" + validateBP _ fun_str (Just modl) = do + isInterpr <- GHC.moduleIsInterpreted modl + (_, _, decls) <- getModBreak modl + mb_err_msg <- case isInterpr of + False -> pure $ Just $ text "Module" <+> quotes (ppr modl) + <+> text "is not interpreted" + True -> case fun_str `elem` (declPath <$> elems decls) of + False -> pure $ Just $ + text "No breakpoint found for" <+> quotes (text fun_str) + <+> "in module" <+> quotes (ppr modl) + True -> pure Nothing + pure mb_err_msg + breakSyntax :: a -breakSyntax = throwGhcException (CmdLineError "Syntax: :break [<mod>] <line> [<column>]") +breakSyntax = throwGhcException $ CmdLineError ("Syntax: :break [<mod>.]<func>[.<func>]\n" + ++ " :break [<mod>] <line> [<column>]") findBreakAndSet :: GhciMonad m => Module -> (TickArray -> [(Int, RealSrcSpan)]) -> m () @@ -3870,15 +3924,15 @@ findBreakByLine line arr -- The aim is to find the breakpoints for all the RHSs of the -- equations corresponding to a binding. So we find all breakpoints -- for --- (a) this binder only (not a nested declaration) +-- (a) this binder only (it maybe a top-level or a nested declaration) -- (b) that do not have an enclosing breakpoint -findBreakForBind :: Name -> GHC.ModBreaks -> TickArray +findBreakForBind :: String -> GHC.ModBreaks -> TickArray -> [(BreakIndex,RealSrcSpan)] -findBreakForBind name modbreaks _ = filter (not . enclosed) ticks +findBreakForBind str_name modbreaks _ = filter (not . enclosed) ticks where ticks = [ (index, span) - | (index, [n]) <- assocs (GHC.modBreaks_decls modbreaks), - n == occNameString (nameOccName name), + | (index, decls) <- assocs (GHC.modBreaks_decls modbreaks), + str_name == declPath decls, RealSrcSpan span _ <- [GHC.modBreaks_locs modbreaks ! index] ] enclosed (_,sp0) = any subspan ticks where subspan (_,sp) = sp /= sp0 && @@ -3922,6 +3976,22 @@ start_bold = "\ESC[1m" end_bold :: String end_bold = "\ESC[0m" +{- +Note [Setting Breakpoints by Id] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To set a breakpoint first check whether a ModBreaks array contains a +breakpoint with the given function name: +In `:break M.foo` `M` may be a module name or a local alias of an import +statement. To lookup a breakpoint in the ModBreaks, the effective module +name is needed. Even if a module called `M` exists, `M` may still be +a local alias. To get the module name, parse the top-level identifier with +`GHC.parseName`. If this succeeds, extract the module name from the +returned value. If it fails, catch the exception and assume `M` is a real +module name. + +The names of nested functions are stored in `ModBreaks.modBreaks_decls`. +-} + ----------------------------------------------------------------------------- -- :where @@ -4211,6 +4281,14 @@ lookupModuleName mName = GHC.lookupModule mName Nothing isMainUnitModule :: Module -> Bool isMainUnitModule m = GHC.moduleUnit m == mainUnit +showModule :: Module -> String +showModule = moduleNameString . moduleName + +-- Return a String with the declPath of the function of a breakpoint. +-- See Note [Field modBreaks_decls] in GHC.ByteCode.Types +declPath :: [String] -> String +declPath = intercalate "." + -- TODO: won't work if home dir is encoded. -- (changeDirectory may not work either in that case.) expandPath :: MonadIO m => String -> m String @@ -4267,3 +4345,37 @@ clearAllTargets = discardActiveBreakPoints >> GHC.setTargets [] >> GHC.load LoadAllTargets >> pure () + +-- Split up a string with an eventually qualified declaration name into 3 components +-- 1. module name +-- 2. top-level decl +-- 3. full-name of the eventually nested decl, but without module qualification +-- eg "foo" = ("", "foo", "foo") +-- "A.B.C.foo" = ("A.B.C", "foo", "foo") +-- "M.N.foo.bar" = ("M.N", "foo", "foo.bar") +splitIdent :: String -> (String, String, String) +splitIdent [] = ("", "", "") +splitIdent inp@(a : _) + | (isUpper a) = case fixs of + [] -> (inp, "", "") + (i1 : [] ) -> (upto i1, from i1, from i1) + (i1 : i2 : _) -> (upto i1, take (i2 - i1 - 1) (from i1), from i1) + | otherwise = case ixs of + [] -> ("", inp, inp) + (i1 : _) -> ("", upto i1, inp) + where + ixs = elemIndices '.' inp -- indices of '.' in whole input + fixs = dropWhile isNextUc ixs -- indices of '.' in function names -- + isNextUc ix = isUpper $ safeInp !! (ix+1) + safeInp = inp ++ " " + upto i = take i inp + from i = drop (i + 1) inp + +-- Qualify an identifier name with a module name +-- combineModIdent "A" "foo" = "A.foo" +-- combineModIdent "" "foo" = "foo" +combineModIdent :: String -> String -> String +combineModIdent mod ident + | null mod = ident + | null ident = mod + | otherwise = mod ++ "." ++ ident diff --git a/testsuite/tests/ghci.debugger/scripts/T17989.stdout b/testsuite/tests/ghci.debugger/scripts/T17989.stdout index ce658ace22..15ac9b62b9 100644 --- a/testsuite/tests/ghci.debugger/scripts/T17989.stdout +++ b/testsuite/tests/ghci.debugger/scripts/T17989.stdout @@ -1,9 +1,20 @@ -7 7 ":break " +18 18 ":break " "B.bar" "B.foo" +"B.foo.x" +"B.foo.y" "T17989A.bar" "T17989A.foo" +"T17989A.foo.x" +"T17989A.foo.y" +"T17989A.priv" +"T17989B.bar" +"T17989B.foo" +"T17989B.foo.x" +"T17989B.foo.y" +"T17989B.priv" "T17989C.foo" +"T17989C.priv" "foo" "main" Breakpoint 0 activated at T17989B.hs:10:9-25 @@ -13,8 +24,10 @@ Breakpoint 3 activated at T17989A.hs:4:9-14 Breakpoint 4 activated at T17989C.hs:4:9-26 Breakpoint 4 was already set at T17989C.hs:4:9-26 Breakpoint 5 activated at T17989M.hs:6:8-51 -2 2 ":break " +4 4 ":break " "B.bar" "B.foo" +"B.foo.x" +"B.foo.y" 1 1 ":break " "foo" diff --git a/testsuite/tests/ghci.debugger/scripts/T3000.hs b/testsuite/tests/ghci.debugger/scripts/T3000.hs new file mode 100644 index 0000000000..429b61b836 --- /dev/null +++ b/testsuite/tests/ghci.debugger/scripts/T3000.hs @@ -0,0 +1,4 @@ +import qualified T3000S as S + +main :: IO () +main = putStrLn $ S.sshow 7 diff --git a/testsuite/tests/ghci.debugger/scripts/T3000.script b/testsuite/tests/ghci.debugger/scripts/T3000.script new file mode 100644 index 0000000000..4cee82d4a0 --- /dev/null +++ b/testsuite/tests/ghci.debugger/scripts/T3000.script @@ -0,0 +1,16 @@ +:l T3000 +:break main +:break Main.main +:break T3000S.sshow +:break S.sshow +:break T3000S.hidden +:break T3000S.sshow.nest +:show breaks +-- Generate some error messages +:break xyz +:break sshow +:break S.hidden +:break S.hidden.nest +:break Foo.xyz +:break T3000S +:break T3000S.xyz diff --git a/testsuite/tests/ghci.debugger/scripts/T3000.stdout b/testsuite/tests/ghci.debugger/scripts/T3000.stdout new file mode 100644 index 0000000000..57c8459d81 --- /dev/null +++ b/testsuite/tests/ghci.debugger/scripts/T3000.stdout @@ -0,0 +1,21 @@ +Breakpoint 0 activated at T3000.hs:4:8-27 +Breakpoint 0 was already set at T3000.hs:4:8-27 +Breakpoint 1 activated at T3000S.hs:9:9-27 +Breakpoint 1 was already set at T3000S.hs:9:9-27 +Breakpoint 2 activated at T3000S.hs:12:12-23 +Breakpoint 3 activated at T3000S.hs:13:12-32 +Breakpoint 4 activated at T3000S.hs:6:18-38 +Breakpoint 5 activated at T3000S.hs:7:18-36 +[0] Main T3000.hs:4:8-27 enabled +[1] T3000S T3000S.hs:9:9-27 enabled +[2] T3000S T3000S.hs:12:12-23 enabled +[3] T3000S T3000S.hs:13:12-32 enabled +[4] T3000S T3000S.hs:6:18-38 enabled +[5] T3000S T3000S.hs:7:18-36 enabled +Cannot set breakpoint on ‘xyz’: ‘xyz’ not in scope +Cannot set breakpoint on ‘sshow’: ‘sshow’ not in scope +Cannot set breakpoint on ‘S.hidden’: ‘S.hidden’ not in scope +Cannot set breakpoint on ‘S.hidden.nest’: ‘S.hidden’ not in scope +Cannot set breakpoint on ‘Foo.xyz’: ‘Foo.xyz’ not in scope +Cannot set breakpoint on ‘T3000S’: Function name is missing +Cannot set breakpoint on ‘T3000S.xyz’: No breakpoint found for ‘xyz’ in module ‘T3000S’ diff --git a/testsuite/tests/ghci.debugger/scripts/T3000S.hs b/testsuite/tests/ghci.debugger/scripts/T3000S.hs new file mode 100644 index 0000000000..245b3963bd --- /dev/null +++ b/testsuite/tests/ghci.debugger/scripts/T3000S.hs @@ -0,0 +1,13 @@ +module T3000S (sshow) where + +sshow :: Int -> String +sshow n = + let nest :: Int -> String + nest 0 = " nest: " ++ hidden 0 + nest k = " nest: " ++ show k + in + " show: " ++ nest n + +hidden :: Int -> String +hidden 1 = " hidden: 1" +hidden n = " hidden: " ++ show n diff --git a/testsuite/tests/ghci.debugger/scripts/all.T b/testsuite/tests/ghci.debugger/scripts/all.T index aa998adef2..bd92d0ffa8 100644 --- a/testsuite/tests/ghci.debugger/scripts/all.T +++ b/testsuite/tests/ghci.debugger/scripts/all.T @@ -109,6 +109,7 @@ test('T1620', extra_files(['T1620/', 'T1620/T1620.hs']), ghci_script, ['T1620.script']) test('T2740', normal, ghci_script, ['T2740.script']) test('T2950', normal, ghci_script, ['T2950.script']) +test('T3000', normal, ghci_script, ['T3000.script']) test('getargs', extra_files(['../getargs.hs']), ghci_script, ['getargs.script']) test('T7386', normal, ghci_script, ['T7386.script']) diff --git a/testsuite/tests/ghci.debugger/scripts/break002.stdout b/testsuite/tests/ghci.debugger/scripts/break002.stdout index 72e0359c7b..40f07d3a1f 100644 --- a/testsuite/tests/ghci.debugger/scripts/break002.stdout +++ b/testsuite/tests/ghci.debugger/scripts/break002.stdout @@ -1,2 +1,2 @@ -cannot set breakpoint on map: module GHC.Base is not interpreted -cannot set breakpoint on map: module GHC.Base is not interpreted +Cannot set breakpoint on ‘Data.List.map’: Module ‘GHC.Base’ is not interpreted +Cannot set breakpoint on ‘Data.List.map’: Module ‘GHC.Base’ is not interpreted diff --git a/testsuite/tests/ghci.debugger/scripts/break019.stderr b/testsuite/tests/ghci.debugger/scripts/break019.stderr deleted file mode 100644 index 3cb286b7eb..0000000000 --- a/testsuite/tests/ghci.debugger/scripts/break019.stderr +++ /dev/null @@ -1,2 +0,0 @@ - -<interactive>:1:1: error: Not in scope: ‘Test2’ diff --git a/testsuite/tests/ghci.debugger/scripts/break019.stdout b/testsuite/tests/ghci.debugger/scripts/break019.stdout new file mode 100644 index 0000000000..4cc0e1d9db --- /dev/null +++ b/testsuite/tests/ghci.debugger/scripts/break019.stdout @@ -0,0 +1 @@ +Cannot set breakpoint on ‘Test2’: Function name is missing |