xref: /llvm-project/lldb/test/API/commands/dwim-print/TestDWIMPrint.py (revision 1250a1db1a378736afda389c94d2846d7a254576)
1"""
2Test dwim-print with variables, variable paths, and expressions.
3"""
4
5import re
6import lldb
7from lldbsuite.test.lldbtest import *
8from lldbsuite.test.decorators import *
9import lldbsuite.test.lldbutil as lldbutil
10
11
12class TestCase(TestBase):
13    def _run_cmd(self, cmd: str) -> str:
14        """Run the given lldb command and return its output."""
15        result = lldb.SBCommandReturnObject()
16        self.ci.HandleCommand(cmd, result)
17        return result.GetOutput().rstrip()
18
19    VAR_IDENT = re.compile(r"(?:\$\d+|[\w.]+) = ")
20
21    def _strip_result_var(self, string: str) -> str:
22        """
23        Strip (persistent) result variables (ex '$0 = ', or 'someVar = ', etc).
24
25        This allows for using the output of `expression`/`frame variable`, to
26        compare it to `dwim-print` output, which disables result variables.
27        """
28        return self.VAR_IDENT.subn("", string, 1)[0]
29
30    def _expect_cmd(
31        self,
32        dwim_cmd: str,
33        actual_cmd: str,
34    ) -> None:
35        """Run dwim-print and verify the output against the expected command."""
36        # Resolve the dwim-print command to either `expression` or `frame variable`.
37        substitute_cmd = dwim_cmd.replace("dwim-print", actual_cmd, 1)
38        interp = self.dbg.GetCommandInterpreter()
39        result = lldb.SBCommandReturnObject()
40        interp.ResolveCommand(substitute_cmd, result)
41        self.assertTrue(result.Succeeded(), result.GetError())
42
43        resolved_cmd = result.GetOutput()
44        if actual_cmd == "frame variable":
45            resolved_cmd = resolved_cmd.replace(" -- ", " ", 1)
46
47        resolved_cmd_output = self._run_cmd(resolved_cmd)
48        dwim_cmd_output = self._strip_result_var(resolved_cmd_output)
49
50        # Verify dwim-print chose the expected command.
51        self.runCmd("settings set dwim-print-verbosity full")
52
53        self.expect(
54            dwim_cmd,
55            substrs=[
56                f"note: ran `{resolved_cmd}`",
57                dwim_cmd_output,
58            ],
59        )
60
61    def test_variables(self):
62        """Test dwim-print with variables."""
63        self.build()
64        lldbutil.run_to_name_breakpoint(self, "main")
65        vars = ("argc", "argv")
66        for var in vars:
67            self._expect_cmd(f"dwim-print {var}", "frame variable")
68
69    def test_variable_paths(self):
70        """Test dwim-print with variable path expressions."""
71        self.build()
72        lldbutil.run_to_name_breakpoint(self, "main")
73        exprs = ("&argc", "*argv", "argv[0]")
74        for expr in exprs:
75            self._expect_cmd(f"dwim-print {expr}", "expression")
76
77    def test_expressions(self):
78        """Test dwim-print with expressions."""
79        self.build()
80        lldbutil.run_to_name_breakpoint(self, "main")
81        exprs = ("argc + 1", "(void)argc", "(int)abs(argc)")
82        for expr in exprs:
83            self._expect_cmd(f"dwim-print {expr}", "expression")
84
85    def test_dummy_target_expressions(self):
86        """Test dwim-print's ability to evaluate expressions without a target."""
87        self._expect_cmd("dwim-print 1 + 2", "expression")
88
89    def test_gdb_format(self):
90        self.build()
91        lldbutil.run_to_name_breakpoint(self, "main")
92        self._expect_cmd(f"dwim-print/x argc", "frame variable")
93        self._expect_cmd(f"dwim-print/x argc + 1", "expression")
94
95    def test_format_flags(self):
96        self.build()
97        lldbutil.run_to_name_breakpoint(self, "main")
98        self._expect_cmd(f"dwim-print -fx -- argc", "frame variable")
99        self._expect_cmd(f"dwim-print -fx -- argc + 1", "expression")
100
101    def test_display_flags(self):
102        self.build()
103        lldbutil.run_to_name_breakpoint(self, "main")
104        self._expect_cmd(f"dwim-print -T -- argc", "frame variable")
105        self._expect_cmd(f"dwim-print -T -- argc + 1", "expression")
106
107    def test_expression_language(self):
108        """Test that the language flag doesn't affect the choice of command."""
109        self.build()
110        lldbutil.run_to_name_breakpoint(self, "main")
111        self._expect_cmd(f"dwim-print -l c++ -- argc", "frame variable")
112        self._expect_cmd(f"dwim-print -l c++ -- argc + 1", "expression")
113
114    def test_empty_expression(self):
115        self.build()
116        lldbutil.run_to_name_breakpoint(self, "main")
117        error_msg = "error: 'dwim-print' takes a variable or expression"
118        self.expect(f"dwim-print", error=True, startstr=error_msg)
119        self.expect(f"dwim-print -- ", error=True, startstr=error_msg)
120
121    def test_nested_values(self):
122        """Test dwim-print with nested values (structs, etc)."""
123        self.build()
124        lldbutil.run_to_source_breakpoint(
125            self, "break here", lldb.SBFileSpec("main.cpp")
126        )
127        self.runCmd("settings set auto-one-line-summaries false")
128        self._expect_cmd(f"dwim-print s", "frame variable")
129        self._expect_cmd(f"dwim-print (struct Structure)s", "expression")
130
131    def test_summary_strings(self):
132        """Test dwim-print with type summaries."""
133        self.build()
134        lldbutil.run_to_source_breakpoint(
135            self, "break here", lldb.SBFileSpec("main.cpp")
136        )
137        self.runCmd("settings set auto-one-line-summaries false")
138        self.runCmd("type summary add -e -s 'stub summary' Structure")
139        self._expect_cmd(f"dwim-print s", "frame variable")
140        self._expect_cmd(f"dwim-print (struct Structure)s", "expression")
141        self.runCmd("type summary delete Structure")
142
143    def test_void_result(self):
144        """Test dwim-print does not surface an error message for void expressions."""
145        self.build()
146        lldbutil.run_to_source_breakpoint(
147            self, "break here", lldb.SBFileSpec("main.cpp")
148        )
149        self.expect("dwim-print (void)15", matching=False, patterns=["(?i)error"])
150
151    def test_preserves_persistent_variables(self):
152        """Test dwim-print does not delete persistent variables."""
153        self.build()
154        lldbutil.run_to_source_breakpoint(
155            self, "break here", lldb.SBFileSpec("main.cpp")
156        )
157        self.expect("dwim-print int $i = 15")
158        # Run the same expression twice and verify success. This ensures the
159        # first command does not delete the persistent variable.
160        for _ in range(2):
161            self.expect("dwim-print $i", startstr="(int) 15")
162
163    def test_missing_type(self):
164        """The expected output of po opaque is its address (no error)"""
165        self.build()
166        lldbutil.run_to_source_breakpoint(
167            self, "break here", lldb.SBFileSpec("main.cpp")
168        )
169        self.expect("dwim-print -O -- opaque", substrs=["0x"])
170
171    def test_variable_expression_path(self):
172        """Test dwim-print supports certain variable expression paths."""
173        self.build()
174        lldbutil.run_to_source_breakpoint(
175            self, "break here", lldb.SBFileSpec("main.cpp")
176        )
177        self.runCmd("settings set auto-one-line-summaries false")
178        self._expect_cmd("dwim-print w.s", "frame variable")
179        self._expect_cmd("dwim-print wp->s", "expression")
180
181    def test_direct_child_access(self):
182        """Test dwim-print supports accessing members/ivars without qualification."""
183        self.build()
184        lldbutil.run_to_source_breakpoint(
185            self, "break inside", lldb.SBFileSpec("main.cpp")
186        )
187        self._expect_cmd("dwim-print number", "frame variable")
188