Commit 7e6d3d09 authored by Roland Senn's avatar Roland Senn Committed by Marge Bot

In `:break ident` allow out of scope and nested identifiers (Fix #3000)

This patch fixes the bug and implements the feature request of #3000.

1. If `Module` is a real module name and `identifier` a name of a
top-level function in `Module` then `:break Module.identifer` works
also for an `identifier` that is out of scope.

2. Extend the syntax for `:break identifier` to:

    :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.

3. To support the new functionality rewrite the code to tab complete `:break`.
parent 03a708ba
...@@ -154,6 +154,7 @@ data ModBreaks ...@@ -154,6 +154,7 @@ data ModBreaks
-- ^ An array giving the names of the free variables at each breakpoint. -- ^ An array giving the names of the free variables at each breakpoint.
, modBreaks_decls :: !(Array BreakIndex [String]) , modBreaks_decls :: !(Array BreakIndex [String])
-- ^ An array giving the names of the declarations enclosing each breakpoint. -- ^ An array giving the names of the declarations enclosing each breakpoint.
-- See Note [Field modBreaks_decls]
, modBreaks_ccs :: !(Array BreakIndex (RemotePtr CostCentre)) , modBreaks_ccs :: !(Array BreakIndex (RemotePtr CostCentre))
-- ^ Array pointing to cost centre for each breakpoint -- ^ Array pointing to cost centre for each breakpoint
, modBreaks_breakInfo :: IntMap CgBreakInfo , modBreaks_breakInfo :: IntMap CgBreakInfo
...@@ -180,3 +181,12 @@ emptyModBreaks = ModBreaks ...@@ -180,3 +181,12 @@ emptyModBreaks = ModBreaks
, modBreaks_ccs = array (0,-1) [] , modBreaks_ccs = array (0,-1) []
, modBreaks_breakInfo = IntMap.empty , 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".
-}
...@@ -161,6 +161,11 @@ GHCi ...@@ -161,6 +161,11 @@ GHCi
passed as arguments: either by enclosing the file names in double quotes or by 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`) 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 Runtime system
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
......
...@@ -1524,8 +1524,48 @@ breakpoint is to name a top-level function: ...@@ -1524,8 +1524,48 @@ breakpoint is to name a top-level function:
Where ⟨identifier⟩ names any top-level function in an interpreted module Where ⟨identifier⟩ names any top-level function in an interpreted module
currently loaded into GHCi (qualified names may be used). The breakpoint 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 will be set on the body of the function, when it is fully applied.
before any pattern matching has taken place. 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: Breakpoints can also be set by line (and optionally column) number:
......
This diff is collapsed.
7 7 ":break " 18 18 ":break "
"B.bar" "B.bar"
"B.foo" "B.foo"
"B.foo.x"
"B.foo.y"
"T17989A.bar" "T17989A.bar"
"T17989A.foo" "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.foo"
"T17989C.priv"
"foo" "foo"
"main" "main"
Breakpoint 0 activated at T17989B.hs:10:9-25 Breakpoint 0 activated at T17989B.hs:10:9-25
...@@ -13,8 +24,10 @@ Breakpoint 3 activated at T17989A.hs:4:9-14 ...@@ -13,8 +24,10 @@ Breakpoint 3 activated at T17989A.hs:4:9-14
Breakpoint 4 activated at T17989C.hs:4:9-26 Breakpoint 4 activated at T17989C.hs:4:9-26
Breakpoint 4 was already set 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 Breakpoint 5 activated at T17989M.hs:6:8-51
2 2 ":break " 4 4 ":break "
"B.bar" "B.bar"
"B.foo" "B.foo"
"B.foo.x"
"B.foo.y"
1 1 ":break " 1 1 ":break "
"foo" "foo"
import qualified T3000S as S
main :: IO ()
main = putStrLn $ S.sshow 7
: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
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’
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
...@@ -109,6 +109,7 @@ test('T1620', extra_files(['T1620/', 'T1620/T1620.hs']), ...@@ -109,6 +109,7 @@ test('T1620', extra_files(['T1620/', 'T1620/T1620.hs']),
ghci_script, ['T1620.script']) ghci_script, ['T1620.script'])
test('T2740', normal, ghci_script, ['T2740.script']) test('T2740', normal, ghci_script, ['T2740.script'])
test('T2950', normal, ghci_script, ['T2950.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('getargs', extra_files(['../getargs.hs']), ghci_script, ['getargs.script'])
test('T7386', normal, ghci_script, ['T7386.script']) test('T7386', normal, ghci_script, ['T7386.script'])
......
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 map: module GHC.Base is not interpreted Cannot set breakpoint on ‘Data.List.map’: Module ‘GHC.Base’ is not interpreted
<interactive>:1:1: error: Not in scope: ‘Test2’
Cannot set breakpoint on ‘Test2’: Function name is missing
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment