Use anyhow more idiomatically (#31052)

https://github.com/zed-industries/zed/issues/30972 brought up another
case where our context is not enough to track the actual source of the
issue: we get a general top-level error without inner error.

The reason for this was `.ok_or_else(|| anyhow!("failed to read HEAD
SHA"))?; ` on the top level.

The PR finally reworks the way we use anyhow to reduce such issues (or
at least make it simpler to bubble them up later in a fix).
On top of that, uses a few more anyhow methods for better readability.

* `.ok_or_else(|| anyhow!("..."))`, `map_err` and other similar error
conversion/option reporting cases are replaced with `context` and
`with_context` calls
* in addition to that, various `anyhow!("failed to do ...")` are
stripped with `.context("Doing ...")` messages instead to remove the
parasitic `failed to` text
* `anyhow::ensure!` is used instead of `if ... { return Err(...); }`
calls
* `anyhow::bail!` is used instead of `return Err(anyhow!(...));`

Release Notes:

- N/A
This commit is contained in:
Kirill Bulatov 2025-05-21 02:06:07 +03:00 committed by GitHub
parent 1e51a7ac44
commit 16366cf9f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
294 changed files with 2037 additions and 2610 deletions

View file

@ -1204,7 +1204,7 @@ impl LocalLspStore {
buffer.finalize_last_transaction();
let transaction_id = buffer
.start_transaction()
.ok_or_else(|| anyhow!("transaction already open"))?;
.context("transaction already open")?;
let transaction = buffer
.get_transaction(transaction_id)
.expect("transaction started")
@ -1862,14 +1862,14 @@ impl LocalLspStore {
let capabilities = &language_server.capabilities();
let range_formatting_provider = capabilities.document_range_formatting_provider.as_ref();
if range_formatting_provider.map_or(false, |provider| provider == &OneOf::Left(false)) {
return Err(anyhow!(
anyhow::bail!(
"{} language server does not support range formatting",
language_server.name()
));
);
}
let uri = lsp::Url::from_file_path(abs_path)
.map_err(|_| anyhow!("failed to convert abs path to uri"))?;
.map_err(|()| anyhow!("failed to convert abs path to uri"))?;
let text_document = lsp::TextDocumentIdentifier::new(uri);
let lsp_edits = {
@ -1934,7 +1934,7 @@ impl LocalLspStore {
zlog::info!(logger => "Formatting via LSP");
let uri = lsp::Url::from_file_path(abs_path)
.map_err(|_| anyhow!("failed to convert abs path to uri"))?;
.map_err(|()| anyhow!("failed to convert abs path to uri"))?;
let text_document = lsp::TextDocumentIdentifier::new(uri);
let capabilities = &language_server.capabilities();
@ -2026,10 +2026,7 @@ impl LocalLspStore {
.stderr(smol::process::Stdio::piped())
.spawn()?;
let stdin = child
.stdin
.as_mut()
.ok_or_else(|| anyhow!("failed to acquire stdin"))?;
let stdin = child.stdin.as_mut().context("failed to acquire stdin")?;
let text = buffer
.handle
.update(cx, |buffer, _| buffer.as_rope().clone())?;
@ -2039,14 +2036,13 @@ impl LocalLspStore {
stdin.flush().await?;
let output = child.output().await?;
if !output.status.success() {
return Err(anyhow!(
"command failed with exit code {:?}:\nstdout: {}\nstderr: {}",
output.status.code(),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
));
}
anyhow::ensure!(
output.status.success(),
"command failed with exit code {:?}:\nstdout: {}\nstderr: {}",
output.status.code(),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
let stdout = String::from_utf8(output.stdout)?;
Ok(Some(
@ -2570,9 +2566,7 @@ impl LocalLspStore {
// We detect this case and treat it as if the version was `None`.
return Ok(buffer.read(cx).text_snapshot());
} else {
return Err(anyhow!(
"no snapshots found for buffer {buffer_id} and server {server_id}"
));
anyhow::bail!("no snapshots found for buffer {buffer_id} and server {server_id}");
};
let found_snapshot = snapshots
@ -2617,7 +2611,7 @@ impl LocalLspStore {
push_to_history: bool,
project_transaction: &mut ProjectTransaction,
cx: &mut AsyncApp,
) -> Result<(), anyhow::Error> {
) -> anyhow::Result<()> {
for mut action in actions {
Self::try_resolve_code_action(language_server, &mut action)
.await
@ -2846,7 +2840,7 @@ impl LocalLspStore {
let abs_path = op
.uri
.to_file_path()
.map_err(|_| anyhow!("can't convert URI to path"))?;
.map_err(|()| anyhow!("can't convert URI to path"))?;
if let Some(parent_path) = abs_path.parent() {
fs.create_dir(parent_path).await?;
@ -2871,11 +2865,11 @@ impl LocalLspStore {
let source_abs_path = op
.old_uri
.to_file_path()
.map_err(|_| anyhow!("can't convert URI to path"))?;
.map_err(|()| anyhow!("can't convert URI to path"))?;
let target_abs_path = op
.new_uri
.to_file_path()
.map_err(|_| anyhow!("can't convert URI to path"))?;
.map_err(|()| anyhow!("can't convert URI to path"))?;
fs.rename(
&source_abs_path,
&target_abs_path,
@ -2893,7 +2887,7 @@ impl LocalLspStore {
let abs_path = op
.uri
.to_file_path()
.map_err(|_| anyhow!("can't convert URI to path"))?;
.map_err(|()| anyhow!("can't convert URI to path"))?;
let options = op
.options
.map(|options| fs::RemoveOptions {
@ -3042,12 +3036,10 @@ impl LocalLspStore {
adapter: Arc<CachedLspAdapter>,
cx: &mut AsyncApp,
) -> Result<lsp::ApplyWorkspaceEditResponse> {
let this = this
.upgrade()
.ok_or_else(|| anyhow!("project project closed"))?;
let this = this.upgrade().context("project project closed")?;
let language_server = this
.update(cx, |this, _| this.language_server_for_id(server_id))?
.ok_or_else(|| anyhow!("language server not found"))?;
.context("language server not found")?;
let transaction = Self::deserialize_workspace_edit(
this.clone(),
params.edit,
@ -4372,13 +4364,13 @@ impl LspStore {
err
);
log::warn!("{message}");
anyhow!(message)
anyhow::anyhow!(message)
})?;
let response = request
.response_from_lsp(
response,
this.upgrade().ok_or_else(|| anyhow!("no app context"))?,
this.upgrade().context("no app context")?,
buffer_handle,
language_server.server_id(),
cx.clone(),
@ -4591,7 +4583,7 @@ impl LspStore {
.request(request)
.await?
.transaction
.ok_or_else(|| anyhow!("missing transaction"))?;
.context("missing transaction")?;
buffer_store
.update(cx, |buffer_store, cx| {
@ -4613,7 +4605,7 @@ impl LspStore {
if let Some(edit) = action.lsp_action.edit() {
if edit.changes.is_some() || edit.document_changes.is_some() {
return LocalLspStore::deserialize_workspace_edit(
this.upgrade().ok_or_else(|| anyhow!("no app present"))?,
this.upgrade().context("no app present")?,
edit.clone(),
push_to_history,
lsp_adapter.clone(),
@ -5715,7 +5707,7 @@ impl LspStore {
LspCommand::response_from_proto(
lsp_request,
response,
project.upgrade().ok_or_else(|| anyhow!("No project"))?,
project.upgrade().context("No project")?,
buffer_handle.clone(),
cx.clone(),
)
@ -6525,7 +6517,7 @@ impl LspStore {
mut diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
filter: F,
cx: &mut Context<Self>,
) -> Result<(), anyhow::Error> {
) -> anyhow::Result<()> {
let Some((worktree, relative_path)) =
self.worktree_store.read(cx).find_worktree(&abs_path, cx)
else {
@ -6730,7 +6722,7 @@ impl LspStore {
let abs_path = abs_path
.to_file_path()
.map_err(|_| anyhow!("can't convert URI to path"))?;
.map_err(|()| anyhow!("can't convert URI to path"))?;
let p = abs_path.clone();
let yarn_worktree = lsp_store
.update(cx, move |lsp_store, cx| match lsp_store.as_local() {
@ -7094,12 +7086,8 @@ impl LspStore {
mut cx: AsyncApp,
) -> Result<proto::ApplyCodeActionResponse> {
let sender_id = envelope.original_sender_id().unwrap_or_default();
let action = Self::deserialize_code_action(
envelope
.payload
.action
.ok_or_else(|| anyhow!("invalid action"))?,
)?;
let action =
Self::deserialize_code_action(envelope.payload.action.context("invalid action")?)?;
let apply_code_action = this.update(&mut cx, |this, cx| {
let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
@ -7198,7 +7186,7 @@ impl LspStore {
)
})
})?
.ok_or_else(|| anyhow!("worktree not found"))?;
.context("worktree not found")?;
let (old_abs_path, new_abs_path) = {
let root_path = worktree.update(&mut cx, |this, _| this.abs_path())?;
let new_path = PathBuf::from_proto(envelope.payload.new_path.clone());
@ -7288,10 +7276,7 @@ impl LspStore {
envelope: TypedEnvelope<proto::StartLanguageServer>,
mut cx: AsyncApp,
) -> Result<()> {
let server = envelope
.payload
.server
.ok_or_else(|| anyhow!("invalid server"))?;
let server = envelope.payload.server.context("invalid server")?;
this.update(&mut cx, |this, cx| {
let server_id = LanguageServerId(server.id as usize);
@ -7322,11 +7307,7 @@ impl LspStore {
this.update(&mut cx, |this, cx| {
let language_server_id = LanguageServerId(envelope.payload.language_server_id as usize);
match envelope
.payload
.variant
.ok_or_else(|| anyhow!("invalid variant"))?
{
match envelope.payload.variant.context("invalid variant")? {
proto::update_language_server::Variant::WorkStart(payload) => {
this.on_lsp_work_start(
language_server_id,
@ -7903,11 +7884,11 @@ impl LspStore {
let completion = this
.read_with(&cx, |this, cx| {
let id = LanguageServerId(envelope.payload.language_server_id as usize);
let Some(server) = this.language_server_for_id(id) else {
return Err(anyhow!("No language server {id}"));
};
let server = this
.language_server_for_id(id)
.with_context(|| format!("No language server {id}"))?;
Ok(cx.background_spawn(async move {
anyhow::Ok(cx.background_spawn(async move {
let can_resolve = server
.capabilities()
.completion_provider
@ -7994,8 +7975,8 @@ impl LspStore {
.payload
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
Ok::<_, anyhow::Error>(this.apply_on_type_formatting(
.context("invalid position")?;
anyhow::Ok(this.apply_on_type_formatting(
buffer,
position,
envelope.payload.trigger.clone(),
@ -8114,18 +8095,12 @@ impl LspStore {
mut cx: AsyncApp,
) -> Result<proto::OpenBufferForSymbolResponse> {
let peer_id = envelope.original_sender_id().unwrap_or_default();
let symbol = envelope
.payload
.symbol
.ok_or_else(|| anyhow!("invalid symbol"))?;
let symbol = envelope.payload.symbol.context("invalid symbol")?;
let symbol = Self::deserialize_symbol(symbol)?;
let symbol = this.update(&mut cx, |this, _| {
let signature = this.symbol_signature(&symbol.path);
if signature == symbol.signature {
Ok(symbol)
} else {
Err(anyhow!("invalid symbol signature"))
}
anyhow::ensure!(signature == symbol.signature, "invalid symbol signature");
Ok(symbol)
})??;
let buffer = this
.update(&mut cx, |this, cx| {
@ -8268,10 +8243,7 @@ impl LspStore {
let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
let buffer = this.buffer_store.read(cx).get_existing(buffer_id)?;
let completion = Self::deserialize_completion(
envelope
.payload
.completion
.ok_or_else(|| anyhow!("invalid completion"))?,
envelope.payload.completion.context("invalid completion")?,
)?;
anyhow::Ok((buffer, completion))
})??;
@ -8365,10 +8337,7 @@ impl LspStore {
let ranges = match &target {
LspFormatTarget::Buffers => None,
LspFormatTarget::Ranges(ranges) => {
let Some(ranges) = ranges.get(&id) else {
return Err(anyhow!("No format ranges provided for buffer"));
};
Some(ranges.clone())
Some(ranges.get(&id).context("No format ranges provided for buffer")?.clone())
}
};
@ -8498,17 +8467,20 @@ impl LspStore {
buffers.insert(this.buffer_store.read(cx).get_existing(buffer_id)?);
}
let kind = match envelope.payload.kind.as_str() {
"" => Ok(CodeActionKind::EMPTY),
"quickfix" => Ok(CodeActionKind::QUICKFIX),
"refactor" => Ok(CodeActionKind::REFACTOR),
"refactor.extract" => Ok(CodeActionKind::REFACTOR_EXTRACT),
"refactor.inline" => Ok(CodeActionKind::REFACTOR_INLINE),
"refactor.rewrite" => Ok(CodeActionKind::REFACTOR_REWRITE),
"source" => Ok(CodeActionKind::SOURCE),
"source.organizeImports" => Ok(CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
"source.fixAll" => Ok(CodeActionKind::SOURCE_FIX_ALL),
_ => Err(anyhow!("Invalid code action kind")),
}?;
"" => CodeActionKind::EMPTY,
"quickfix" => CodeActionKind::QUICKFIX,
"refactor" => CodeActionKind::REFACTOR,
"refactor.extract" => CodeActionKind::REFACTOR_EXTRACT,
"refactor.inline" => CodeActionKind::REFACTOR_INLINE,
"refactor.rewrite" => CodeActionKind::REFACTOR_REWRITE,
"source" => CodeActionKind::SOURCE,
"source.organizeImports" => CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
"source.fixAll" => CodeActionKind::SOURCE_FIX_ALL,
_ => anyhow::bail!(
"Invalid code action kind {}",
envelope.payload.kind.as_str()
),
};
anyhow::Ok(this.apply_code_action_kind(buffers, kind, false, cx))
})??;
@ -8778,7 +8750,7 @@ impl LspStore {
let abs_path = params
.uri
.to_file_path()
.map_err(|_| anyhow!("URI is not a file"))?;
.map_err(|()| anyhow!("URI is not a file"))?;
let mut diagnostics = Vec::default();
let mut primary_diagnostic_group_ids = HashMap::default();
let mut sources_by_group_id = HashMap::default();
@ -9320,12 +9292,8 @@ impl LspStore {
path: Arc::<Path>::from_proto(serialized_symbol.path),
};
let start = serialized_symbol
.start
.ok_or_else(|| anyhow!("invalid start"))?;
let end = serialized_symbol
.end
.ok_or_else(|| anyhow!("invalid end"))?;
let start = serialized_symbol.start.context("invalid start")?;
let end = serialized_symbol.end.context("invalid end")?;
Ok(CoreSymbol {
language_server_name: LanguageServerName(serialized_symbol.language_server_name.into()),
source_worktree_id,
@ -10307,15 +10275,14 @@ impl LspAdapterDelegate for LocalLspAdapterDelegate {
.output()
.await?;
if output.status.success() {
return Ok(());
}
Err(anyhow!(
anyhow::ensure!(
output.status.success(),
"{}, stdout: {:?}, stderr: {:?}",
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
))
);
Ok(())
}
fn update_status(&self, server_name: LanguageServerName, status: language::BinaryStatus) {