Skip to content

Commit 6fbe0e1

Browse files
show more detailed error messages depending on what went wrong
1 parent 3890c17 commit 6fbe0e1

File tree

5 files changed

+269
-139
lines changed

5 files changed

+269
-139
lines changed

lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ import * as util from "util";
33
import * as vscode from "vscode";
44
import * as child_process from "child_process";
55
import * as fs from "node:fs/promises";
6-
import {
7-
showErrorWithConfigureButton,
8-
showLLDBDapNotFoundMessage,
9-
} from "./ui/error-messages";
6+
import { ConfigureButton, OpenSettingsButton } from "./ui/show-error-message";
7+
import { ErrorWithNotification } from "./ui/error-with-notification";
108

119
const exec = util.promisify(child_process.execFile);
1210

@@ -69,10 +67,19 @@ async function findDAPExecutable(): Promise<string | undefined> {
6967
return undefined;
7068
}
7169

70+
/**
71+
* Retrieves the lldb-dap executable path either from settings or the provided
72+
* {@link vscode.DebugConfiguration}.
73+
*
74+
* @param workspaceFolder The {@link vscode.WorkspaceFolder} that the debug session will be launched within
75+
* @param configuration The {@link vscode.DebugConfiguration} that will be launched
76+
* @throws An {@link ErrorWithNotification} if something went wrong
77+
* @returns The path to the lldb-dap executable
78+
*/
7279
async function getDAPExecutable(
7380
workspaceFolder: vscode.WorkspaceFolder | undefined,
7481
configuration: vscode.DebugConfiguration,
75-
): Promise<string | undefined> {
82+
): Promise<string> {
7683
// Check if the executable was provided in the launch configuration.
7784
const launchConfigPath = configuration["debugAdapterExecutable"];
7885
if (typeof launchConfigPath === "string" && launchConfigPath.length !== 0) {
@@ -92,36 +99,39 @@ async function getDAPExecutable(
9299
return foundPath;
93100
}
94101

95-
return undefined;
102+
throw new ErrorWithNotification(
103+
"Unable to find the path to the LLDB debug adapter executable.",
104+
new OpenSettingsButton("lldb-dap.executable-path"),
105+
);
96106
}
97107

108+
/**
109+
* Retrieves the arguments that will be provided to lldb-dap either from settings or the provided
110+
* {@link vscode.DebugConfiguration}.
111+
*
112+
* @param workspaceFolder The {@link vscode.WorkspaceFolder} that the debug session will be launched within
113+
* @param configuration The {@link vscode.DebugConfiguration} that will be launched
114+
* @throws An {@link ErrorWithNotification} if something went wrong
115+
* @returns The arguments that will be provided to lldb-dap
116+
*/
98117
async function getDAPArguments(
99118
workspaceFolder: vscode.WorkspaceFolder | undefined,
100119
configuration: vscode.DebugConfiguration,
101-
userInteractive?: boolean,
102-
): Promise<string[] | null | undefined> {
120+
): Promise<string[]> {
103121
// Check the debug configuration for arguments first
104122
const debugConfigArgs = configuration.debugAdapterArgs;
105123
if (debugConfigArgs) {
106124
if (
107125
!Array.isArray(debugConfigArgs) ||
108126
debugConfigArgs.findIndex((entry) => typeof entry !== "string") !== -1
109127
) {
110-
if (!userInteractive) {
111-
return undefined;
112-
}
113-
return showErrorWithConfigureButton(
114-
"The debugAdapterArgs property must be an array of string values.",
128+
throw new ErrorWithNotification(
129+
"The debugAdapterArgs property must be an array of string values. Please update your launch configuration",
130+
new ConfigureButton(),
115131
);
116132
}
117133
return debugConfigArgs;
118134
}
119-
if (
120-
Array.isArray(debugConfigArgs) &&
121-
debugConfigArgs.findIndex((entry) => typeof entry !== "string") === -1
122-
) {
123-
return debugConfigArgs;
124-
}
125135
// Fall back on the workspace configuration
126136
return vscode.workspace
127137
.getConfiguration("lldb-dap", workspaceFolder)
@@ -133,15 +143,14 @@ async function getDAPArguments(
133143
* debug configuration. Assumes that the given debug configuration is for a local launch of lldb-dap.
134144
*
135145
* @param workspaceFolder The {@link vscode.WorkspaceFolder} that the debug session will be launched within
136-
* @param configuration The {@link vscode.DebugConfiguration}
137-
* @param userInteractive Whether or not this was called due to user interaction (determines if modals should be shown)
138-
* @returns
146+
* @param configuration The {@link vscode.DebugConfiguration} that will be launched
147+
* @throws An {@link ErrorWithNotification} if something went wrong
148+
* @returns The {@link vscode.DebugAdapterExecutable} that can be used to launch lldb-dap
139149
*/
140150
export async function createDebugAdapterExecutable(
141151
workspaceFolder: vscode.WorkspaceFolder | undefined,
142152
configuration: vscode.DebugConfiguration,
143-
userInteractive?: boolean,
144-
): Promise<vscode.DebugAdapterExecutable | undefined> {
153+
): Promise<vscode.DebugAdapterExecutable> {
145154
const config = vscode.workspace.getConfiguration("lldb-dap", workspaceFolder);
146155
const log_path = config.get<string>("log-path");
147156
let env: { [key: string]: string } = {};
@@ -152,18 +161,16 @@ export async function createDebugAdapterExecutable(
152161
config.get<{ [key: string]: string }>("environment") || {};
153162
const dapPath = await getDAPExecutable(workspaceFolder, configuration);
154163

155-
if (!dapPath) {
156-
if (userInteractive) {
157-
showLLDBDapNotFoundMessage();
158-
}
159-
return undefined;
160-
}
161-
162164
if (!(await isExecutable(dapPath))) {
163-
if (userInteractive) {
164-
showLLDBDapNotFoundMessage(dapPath);
165+
let message = `Debug adapter path "${dapPath}" is not a valid file.`;
166+
let buttons: (OpenSettingsButton | ConfigureButton)[] = [
167+
new OpenSettingsButton("lldb-dap.executable-path"),
168+
];
169+
if ("debugAdapterPath" in configuration) {
170+
message += " The path comes from your launch configuration.";
171+
buttons = [new ConfigureButton()];
165172
}
166-
return undefined;
173+
throw new ErrorWithNotification(message, ...buttons);
167174
}
168175

169176
const dbgOptions = {
@@ -172,14 +179,7 @@ export async function createDebugAdapterExecutable(
172179
...env,
173180
},
174181
};
175-
const dbgArgs = await getDAPArguments(
176-
workspaceFolder,
177-
configuration,
178-
userInteractive,
179-
);
180-
if (!dbgArgs) {
181-
return undefined;
182-
}
182+
const dbgArgs = await getDAPArguments(workspaceFolder, configuration);
183183

184184
return new vscode.DebugAdapterExecutable(dapPath, dbgArgs, dbgOptions);
185185
}

lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts

Lines changed: 61 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import * as child_process from "child_process";
33
import * as util from "util";
44
import { LLDBDapServer } from "./lldb-dap-server";
55
import { createDebugAdapterExecutable } from "./debug-adapter-factory";
6-
import { showErrorWithConfigureButton } from "./ui/error-messages";
6+
import { ConfigureButton, showErrorMessage } from "./ui/show-error-message";
7+
import { ErrorWithNotification } from "./ui/error-with-notification";
78

89
const exec = util.promisify(child_process.execFile);
910

@@ -29,62 +30,74 @@ export class LLDBDapConfigurationProvider
2930
debugConfiguration: vscode.DebugConfiguration,
3031
_token?: vscode.CancellationToken,
3132
): Promise<vscode.DebugConfiguration | null | undefined> {
32-
if (
33-
"debugAdapterHost" in debugConfiguration &&
34-
!("debugAdapterPort" in debugConfiguration)
35-
) {
36-
return showErrorWithConfigureButton(
37-
"A debugAdapterPort must be provided when debugAdapterHost is set. Please update your launch configuration.",
38-
);
39-
}
40-
41-
// Check if we're going to launch a debug session or use an existing process
42-
if ("debugAdapterPort" in debugConfiguration) {
33+
try {
4334
if (
44-
"debugAdapterExecutable" in debugConfiguration ||
45-
"debugAdapterArgs" in debugConfiguration
35+
"debugAdapterHost" in debugConfiguration &&
36+
!("debugAdapterPort" in debugConfiguration)
4637
) {
47-
return showErrorWithConfigureButton(
48-
"The debugAdapterPort property is incompatible with debugAdapterExecutable and debugAdapterArgs. Please update your launch configuration.",
38+
throw new ErrorWithNotification(
39+
"A debugAdapterPort must be provided when debugAdapterHost is set. Please update your launch configuration.",
40+
new ConfigureButton(),
4941
);
5042
}
51-
} else {
52-
// Always try to create the debug adapter executable as this will show the user errors
53-
// if there are any.
54-
const executable = await createDebugAdapterExecutable(
55-
folder,
56-
debugConfiguration,
57-
/* userInteractive */ true,
58-
);
59-
if (!executable) {
60-
return undefined;
61-
}
6243

63-
// Server mode needs to be handled here since DebugAdapterDescriptorFactory
64-
// will show an unhelpful error if it returns undefined. We'd rather show a
65-
// nicer error message here and allow stopping the debug session gracefully.
66-
const config = vscode.workspace.getConfiguration("lldb-dap", folder);
67-
if (
68-
config.get<boolean>("serverMode", false) &&
69-
(await isServerModeSupported(executable.command))
70-
) {
71-
const serverInfo = await this.server.start(
72-
executable.command,
73-
executable.args,
74-
executable.options,
44+
// Check if we're going to launch a debug session or use an existing process
45+
if ("debugAdapterPort" in debugConfiguration) {
46+
if (
47+
"debugAdapterExecutable" in debugConfiguration ||
48+
"debugAdapterArgs" in debugConfiguration
49+
) {
50+
throw new ErrorWithNotification(
51+
"The debugAdapterPort property is incompatible with debugAdapterExecutable and debugAdapterArgs. Please update your launch configuration.",
52+
new ConfigureButton(),
53+
);
54+
}
55+
} else {
56+
// Always try to create the debug adapter executable as this will show the user errors
57+
// if there are any.
58+
const executable = await createDebugAdapterExecutable(
59+
folder,
60+
debugConfiguration,
7561
);
76-
if (!serverInfo) {
62+
if (!executable) {
7763
return undefined;
7864
}
79-
// Use a debug adapter host and port combination rather than an executable
80-
// and list of arguments.
81-
delete debugConfiguration.debugAdapterExecutable;
82-
delete debugConfiguration.debugAdapterArgs;
83-
debugConfiguration.debugAdapterHost = serverInfo.host;
84-
debugConfiguration.debugAdapterPort = serverInfo.port;
65+
66+
// Server mode needs to be handled here since DebugAdapterDescriptorFactory
67+
// will show an unhelpful error if it returns undefined. We'd rather show a
68+
// nicer error message here and allow stopping the debug session gracefully.
69+
const config = vscode.workspace.getConfiguration("lldb-dap", folder);
70+
if (
71+
config.get<boolean>("serverMode", false) &&
72+
(await isServerModeSupported(executable.command))
73+
) {
74+
const serverInfo = await this.server.start(
75+
executable.command,
76+
executable.args,
77+
executable.options,
78+
);
79+
if (!serverInfo) {
80+
return undefined;
81+
}
82+
// Use a debug adapter host and port combination rather than an executable
83+
// and list of arguments.
84+
delete debugConfiguration.debugAdapterExecutable;
85+
delete debugConfiguration.debugAdapterArgs;
86+
debugConfiguration.debugAdapterHost = serverInfo.host;
87+
debugConfiguration.debugAdapterPort = serverInfo.port;
88+
}
8589
}
86-
}
8790

88-
return debugConfiguration;
91+
return debugConfiguration;
92+
} catch (error) {
93+
// Show a better error message to the user if possible
94+
if (!(error instanceof ErrorWithNotification)) {
95+
throw error;
96+
}
97+
return await error.showNotification({
98+
modal: true,
99+
showConfigureButton: true,
100+
});
101+
}
89102
}
90103
}

lldb/tools/lldb-dap/src-ts/ui/error-messages.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)