From c0eaff7dd1fdfffed4e5706780e79967760d1d9b Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Fri, 27 Dec 2024 10:57:08 -0600 Subject: [PATCH 01/98] Rename dependency tree view and dependency provider --- editors/code/src/commands.ts | 10 +++++----- editors/code/src/ctx.ts | 26 +++++++++++++------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 73e39c900e..64af124e72 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -288,13 +288,13 @@ export function openCargoToml(ctx: CtxInit): Cmd { export function revealDependency(ctx: CtxInit): Cmd { return async (editor: RustEditor) => { - if (!ctx.dependencies?.isInitialized()) { + if (!ctx.dependenciesProvider?.isInitialized()) { return; } const documentPath = editor.document.uri.fsPath; - const dep = ctx.dependencies?.getDependency(documentPath); + const dep = ctx.dependenciesProvider?.getDependency(documentPath); if (dep) { - await ctx.treeView?.reveal(dep, { select: true, expand: true }); + await ctx.dependencyTreeView?.reveal(dep, { select: true, expand: true }); } else { await revealParentChain(editor.document, ctx); } @@ -340,10 +340,10 @@ async function revealParentChain(document: RustDocument, ctx: CtxInit) { // a open file referencing the old version return; } - } while (!ctx.dependencies?.contains(documentPath)); + } while (!ctx.dependenciesProvider?.contains(documentPath)); parentChain.reverse(); for (const idx in parentChain) { - const treeView = ctx.treeView; + const treeView = ctx.dependencyTreeView; if (!treeView) { continue; } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 37a54abf71..7b5cc9f106 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -84,8 +84,8 @@ export class Ctx implements RustAnalyzerExtensionApi { private commandFactories: Record; private commandDisposables: Disposable[]; private unlinkedFiles: vscode.Uri[]; - private _dependencies: RustDependenciesProvider | undefined; - private _treeView: vscode.TreeView | undefined; + private _dependenciesProvider: RustDependenciesProvider | undefined; + private _dependencyTreeView: vscode.TreeView | undefined; private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" }; private _serverVersion: string; private statusBarActiveEditorListener: Disposable; @@ -102,12 +102,12 @@ export class Ctx implements RustAnalyzerExtensionApi { return this._client; } - get treeView() { - return this._treeView; + get dependencyTreeView() { + return this._dependencyTreeView; } - get dependencies() { - return this._dependencies; + get dependenciesProvider() { + return this._dependenciesProvider; } constructor( @@ -285,13 +285,13 @@ export class Ctx implements RustAnalyzerExtensionApi { ...this, client: client, }; - this._dependencies = new RustDependenciesProvider(ctxInit); - this._treeView = vscode.window.createTreeView("rustDependencies", { - treeDataProvider: this._dependencies, + this._dependenciesProvider = new RustDependenciesProvider(ctxInit); + this._dependencyTreeView = vscode.window.createTreeView("rustDependencies", { + treeDataProvider: this._dependenciesProvider, showCollapseAll: true, }); - this.pushExtCleanup(this._treeView); + this.pushExtCleanup(this._dependencyTreeView); vscode.window.onDidChangeActiveTextEditor(async (e) => { // we should skip documents that belong to the current workspace if (this.shouldRevealDependency(e)) { @@ -303,7 +303,7 @@ export class Ctx implements RustAnalyzerExtensionApi { } }); - this.treeView?.onDidChangeVisibility(async (e) => { + this.dependencyTreeView?.onDidChangeVisibility(async (e) => { if (e.visible) { const activeEditor = vscode.window.activeTextEditor; if (this.shouldRevealDependency(activeEditor)) { @@ -322,7 +322,7 @@ export class Ctx implements RustAnalyzerExtensionApi { e !== undefined && isRustEditor(e) && !isDocumentInWorkspace(e.document) && - (this.treeView?.visible || false) + (this.dependencyTreeView?.visible || false) ); } @@ -423,7 +423,7 @@ export class Ctx implements RustAnalyzerExtensionApi { } else { statusBar.command = "rust-analyzer.openLogs"; } - this.dependencies?.refresh(); + this.dependenciesProvider?.refresh(); break; case "warning": statusBar.color = new vscode.ThemeColor("statusBarItem.warningForeground"); From 159731022f83da826b1eaebeae44630d43be349a Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Sat, 14 Dec 2024 16:08:22 -0500 Subject: [PATCH 02/98] internal: Generalize `make::expr_from_text` to types which implement `Into` This will help with specializing the various `make::expr_*` functions later --- crates/syntax/src/ast/make.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 282cbc4b3a..0028fb93a5 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -663,7 +663,7 @@ pub fn expr_tuple(elements: impl IntoIterator) -> ast::Expr { pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { expr_from_text(&format!("{lhs} = {rhs}")) } -fn expr_from_text(text: &str) -> ast::Expr { +fn expr_from_text + AstNode>(text: &str) -> E { ast_from_text(&format!("const C: () = {text};")) } pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { From 913d197a04b79e96babba6074bf75b28a82d777a Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Sat, 14 Dec 2024 16:12:25 -0500 Subject: [PATCH 03/98] internal: `make::expr_prefix` should return `ast::PrefixExpr` --- crates/ide-assists/src/handlers/apply_demorgan.rs | 2 +- crates/ide-assists/src/handlers/convert_closure_to_fn.rs | 2 +- crates/ide-assists/src/handlers/replace_if_let_with_match.rs | 2 +- crates/ide-assists/src/utils.rs | 4 ++-- crates/ide-assists/src/utils/ref_field_expr.rs | 2 +- crates/ide-completion/src/completions/postfix.rs | 2 +- crates/syntax/src/ast/make.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index f178a7e0ce..70fb568005 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -252,7 +252,7 @@ fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) { /// Add bang and parentheses to the expression. fn add_bang_paren(expr: ast::Expr) -> ast::Expr { - make::expr_prefix(T![!], make::expr_paren(expr)) + make::expr_prefix(T![!], make::expr_paren(expr)).into() } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 79f303b37a..bb04a43cf9 100644 --- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -507,7 +507,7 @@ fn wrap_capture_in_deref_if_needed( if does_autoderef { return capture_name; } - make::expr_prefix(T![*], capture_name) + make::expr_prefix(T![*], capture_name).into() } fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture) -> ast::Expr { diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index b31d45e6d4..45e3f7a83d 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -272,7 +272,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' ast::Pat::LiteralPat(p) if p.literal().is_some_and(|it| it.token().kind() == T![false]) => { - make::expr_prefix(T![!], scrutinee) + make::expr_prefix(T![!], scrutinee).into() } _ => make::expr_let(if_let_pat, scrutinee).into(), }; diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index e20c4ef09e..78ff441791 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -246,7 +246,7 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { } pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { - invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr)) + invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into()) } fn invert_special_case(expr: &ast::Expr) -> Option { @@ -262,7 +262,7 @@ fn invert_special_case(expr: &ast::Expr) -> Option { T![>] => T![<=], T![>=] => T![<], // Parenthesize other expressions before prefixing `!` - _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))), + _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone())).into()), }; ted::replace(op_token, make::token(rev_token)); Some(bin.into()) diff --git a/crates/ide-assists/src/utils/ref_field_expr.rs b/crates/ide-assists/src/utils/ref_field_expr.rs index e95b291dd7..d434872ea5 100644 --- a/crates/ide-assists/src/utils/ref_field_expr.rs +++ b/crates/ide-assists/src/utils/ref_field_expr.rs @@ -121,7 +121,7 @@ impl RefData { /// Derefs `expr` and wraps it in parens if necessary pub(crate) fn wrap_expr(&self, mut expr: ast::Expr) -> ast::Expr { if self.needs_deref { - expr = make::expr_prefix(T![*], expr); + expr = make::expr_prefix(T![*], expr).into(); } if self.needs_parentheses { diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 7b57eea052..5bdc320d57 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -303,7 +303,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) { resulting_element = ast::Expr::from(parent_deref_element); - new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt); + new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt).into(); } if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) { diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 0028fb93a5..804f76d7e1 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -623,7 +623,7 @@ pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr { expr_from_text(&format!("loop {block}")) } -pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { +pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr { let token = token(op); expr_from_text(&format!("{token}{expr}")) } From c549be9ab631f9944b329b9c2b41936c9bb7ddec Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Sat, 14 Dec 2024 14:31:57 -0500 Subject: [PATCH 04/98] internal: `make::expr_if` should return `ast::IfExpr` --- crates/ide-assists/src/handlers/bool_to_enum.rs | 1 + crates/ide-assists/src/handlers/convert_while_to_loop.rs | 2 +- crates/ide-assists/src/handlers/extract_function.rs | 4 ++-- crates/ide-assists/src/handlers/move_guard.rs | 2 +- crates/ide-assists/src/handlers/replace_if_let_with_match.rs | 2 +- crates/ide-assists/src/handlers/replace_let_with_if_let.rs | 2 +- crates/syntax/src/ast/make.rs | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs index f699899066..cbd3979624 100644 --- a/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -195,6 +195,7 @@ fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr { make::tail_only_block_expr(true_expr), Some(ast::ElseBranch::Block(make::tail_only_block_expr(false_expr))), ) + .into() } } diff --git a/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/crates/ide-assists/src/handlers/convert_while_to_loop.rs index 434daa279c..0b92beefbc 100644 --- a/crates/ide-assists/src/handlers/convert_while_to_loop.rs +++ b/crates/ide-assists/src/handlers/convert_while_to_loop.rs @@ -60,7 +60,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) .indent(while_indent_level); let block_expr = if is_pattern_cond(while_cond.clone()) { let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into())); - let stmts = iter::once(make::expr_stmt(if_expr).into()); + let stmts = iter::once(make::expr_stmt(if_expr.into()).into()); make::block_expr(stmts, None) } else { let if_cond = invert_boolean_expression(while_cond); diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 0d1b6af720..f8315e39a6 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1533,7 +1533,7 @@ impl FlowHandler { .into(), call_expr, ); - make::expr_if(condition.into(), block, None) + make::expr_if(condition.into(), block, None).into() } FlowHandler::IfOption { action } => { let path = make::ext::ident_path("Some"); @@ -1544,7 +1544,7 @@ impl FlowHandler { let action_expr = action.make_result_handler(Some(value)); let action_stmt = make::expr_stmt(action_expr); let then = make::block_expr(iter::once(action_stmt.into()), None); - make::expr_if(cond.into(), then, None) + make::expr_if(cond.into(), then, None).into() } FlowHandler::MatchOption { none } => { let some_name = "value"; diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs index f0c96fe3cb..a487960d8d 100644 --- a/crates/ide-assists/src/handlers/move_guard.rs +++ b/crates/ide-assists/src/handlers/move_guard.rs @@ -61,7 +61,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) }; edit.delete(guard.syntax().text_range()); - edit.replace_ast(arm_expr, if_expr); + edit.replace_ast(arm_expr, if_expr.into()); }, ) } diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 45e3f7a83d..637c6a610c 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -285,7 +285,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' ) .indent(IndentLevel::from_node(match_expr.syntax())); - edit.replace_ast::(match_expr.into(), if_let_expr); + edit.replace_ast::(match_expr.into(), if_let_expr.into()); }, ) } diff --git a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index c2be4593b9..7dbb1f6e24 100644 --- a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -63,7 +63,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> let block = make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax())); let if_ = make::expr_if(make::expr_let(pat, init).into(), block, None); - let stmt = make::expr_stmt(if_); + let stmt = make::expr_stmt(if_.into()); edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); }, diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 804f76d7e1..dc966a81fe 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -607,7 +607,7 @@ pub fn expr_if( condition: ast::Expr, then_branch: ast::BlockExpr, else_branch: Option, -) -> ast::Expr { +) -> ast::IfExpr { let else_branch = match else_branch { Some(ast::ElseBranch::Block(block)) => format!("else {block}"), Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"), From f388482119ba2a39b2c619ad130f06d3c25aa0d0 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:27:31 -0500 Subject: [PATCH 05/98] internal: `make::expr_tuple` should return `ast::TupleExpr` --- crates/ide-assists/src/handlers/extract_function.rs | 6 +++--- crates/ide-assists/src/handlers/remove_dbg.rs | 2 +- crates/ide-assists/src/handlers/unwrap_block.rs | 2 +- crates/ide-assists/src/utils/gen_trait_fn_body.rs | 2 +- crates/syntax/src/ast/make.rs | 2 +- crates/syntax/src/syntax_editor.rs | 3 ++- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index f8315e39a6..5c76db4b7a 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1879,7 +1879,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) - .iter() .map(|var| path_expr_from_local(ctx, var.local, fun.mods.edition)); let expr = make::expr_tuple(exprs); - tail_expr = Some(expr); + tail_expr = Some(expr.into()); } }, }; @@ -2130,13 +2130,13 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option) -> Op make::arg_list(iter::once(make::expr_unit())), ), FlowHandler::IfOption { .. } => { - let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); + let expr = arg_expr.unwrap_or_else(|| make::expr_unit()); let args = make::arg_list(iter::once(expr)); make::expr_call(make::expr_path(make::ext::ident_path("Some")), args) } FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")), FlowHandler::MatchResult { .. } => { - let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); + let expr = arg_expr.unwrap_or_else(|| make::expr_unit()); let args = make::arg_list(iter::once(expr)); make::expr_call(make::expr_path(make::ext::ident_path("Err")), args) } diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index 94274f6d17..31d4a33703 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -152,7 +152,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt exprs => { let exprs = exprs.iter().cloned().map(replace_nested_dbgs); let expr = make::expr_tuple(exprs); - (macro_call.syntax().text_range(), Some(expr)) + (macro_call.syntax().text_range(), Some(expr.into())) } }) } diff --git a/crates/ide-assists/src/handlers/unwrap_block.rs b/crates/ide-assists/src/handlers/unwrap_block.rs index f3e7f5f416..c613947074 100644 --- a/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/crates/ide-assists/src/handlers/unwrap_block.rs @@ -61,7 +61,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option } } None => { - let empty_tuple = make::expr_tuple([]); + let empty_tuple = make::expr_unit(); make::let_stmt(pattern, ty, Some(empty_tuple)).to_string() } }; diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 75caf6d49f..4ba6bd98e4 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -541,7 +541,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) - arms.push(make::match_arm(Some(lhs), None, rhs)); } - let match_target = make::expr_tuple(vec![lhs_name, rhs_name]); + let match_target = make::expr_tuple([lhs_name, rhs_name]).into(); let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); make::expr_match(match_target, list) } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index dc966a81fe..812b0dc888 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -656,7 +656,7 @@ pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr { pub fn expr_paren(expr: ast::Expr) -> ast::Expr { expr_from_text(&format!("({expr})")) } -pub fn expr_tuple(elements: impl IntoIterator) -> ast::Expr { +pub fn expr_tuple(elements: impl IntoIterator) -> ast::TupleExpr { let expr = elements.into_iter().format(", "); expr_from_text(&format!("({expr})")) } diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index 992a847663..81c52e26cf 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs @@ -344,7 +344,8 @@ mod tests { make::expr_literal("2").into(), ), make::expr_literal("true").into(), - ]), + ]) + .into(), ); let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); From 905e1e1fc0f6f7db7bbc1f00b7e593c0f3981df6 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:38:53 -0500 Subject: [PATCH 06/98] internal: move `make::expr_unit` to `make::ext::expr_unit` `expr_unit` is just a shortcut for a common expression, so it belongs in `make::ext` --- .../src/handlers/convert_from_to_tryfrom.rs | 2 +- .../ide-assists/src/handlers/extract_function.rs | 14 +++++++------- crates/ide-assists/src/handlers/remove_dbg.rs | 2 +- .../src/handlers/replace_if_let_with_match.rs | 2 +- crates/ide-assists/src/handlers/unwrap_block.rs | 2 +- crates/syntax/src/ast/edit.rs | 5 +++-- crates/syntax/src/ast/make.rs | 7 +++---- crates/syntax/src/syntax_editor.rs | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index 67c72a93da..dd2e9cbcb5 100644 --- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -97,7 +97,7 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> ); for r in return_exprs { - let t = r.expr().unwrap_or_else(make::expr_unit); + let t = r.expr().unwrap_or_else(make::ext::expr_unit); ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update()); } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 5c76db4b7a..8273ebe310 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1910,7 +1910,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) - match &handler { FlowHandler::None => block, FlowHandler::Try { kind } => { - let block = with_default_tail_expr(block, make::expr_unit()); + let block = with_default_tail_expr(block, make::ext::expr_unit()); map_tail_expr(block, |tail_expr| { let constructor = match kind { TryKind::Option => "Some", @@ -1924,7 +1924,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) - FlowHandler::If { .. } => { let controlflow_continue = make::expr_call( make::expr_path(make::path_from_text("ControlFlow::Continue")), - make::arg_list(iter::once(make::expr_unit())), + make::arg_list([make::ext::expr_unit()]), ); with_tail_expr(block, controlflow_continue) } @@ -2127,17 +2127,17 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option) -> Op FlowHandler::None | FlowHandler::Try { .. } => return None, FlowHandler::If { .. } => make::expr_call( make::expr_path(make::path_from_text("ControlFlow::Break")), - make::arg_list(iter::once(make::expr_unit())), + make::arg_list([make::ext::expr_unit()]), ), FlowHandler::IfOption { .. } => { - let expr = arg_expr.unwrap_or_else(|| make::expr_unit()); - let args = make::arg_list(iter::once(expr)); + let expr = arg_expr.unwrap_or_else(make::ext::expr_unit); + let args = make::arg_list([expr]); make::expr_call(make::expr_path(make::ext::ident_path("Some")), args) } FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")), FlowHandler::MatchResult { .. } => { - let expr = arg_expr.unwrap_or_else(|| make::expr_unit()); - let args = make::arg_list(iter::once(expr)); + let expr = arg_expr.unwrap_or_else(make::ext::expr_unit); + let args = make::arg_list([expr]); make::expr_call(make::expr_path(make::ext::ident_path("Err")), args) } }; diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index 31d4a33703..ae14075c26 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -102,7 +102,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt }; (range, None) }, - _ => (macro_call.syntax().text_range(), Some(make::expr_unit())), + _ => (macro_call.syntax().text_range(), Some(make::ext::expr_unit())), } } } diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 637c6a610c..1a1da6564a 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -179,7 +179,7 @@ fn make_else_arm( [(Either::Right(_), _)] => make::literal_pat("false").into(), _ => make::wildcard_pat().into(), }; - (pattern, make::expr_unit()) + (pattern, make::ext::expr_unit()) }; make::match_arm(iter::once(pattern), None, expr) } diff --git a/crates/ide-assists/src/handlers/unwrap_block.rs b/crates/ide-assists/src/handlers/unwrap_block.rs index c613947074..fd37140e9c 100644 --- a/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/crates/ide-assists/src/handlers/unwrap_block.rs @@ -61,7 +61,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option } } None => { - let empty_tuple = make::expr_unit(); + let empty_tuple = make::ext::expr_unit(); make::let_stmt(pattern, ty, Some(empty_tuple)).to_string() } }; diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index de40d638be..46fdfb65b3 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -153,8 +153,9 @@ impl AstNodeEdit for N {} #[test] fn test_increase_indent() { let arm_list = { - let arm = make::match_arm(iter::once(make::wildcard_pat().into()), None, make::expr_unit()); - make::match_arm_list(vec![arm.clone(), arm]) + let arm = + make::match_arm(iter::once(make::wildcard_pat().into()), None, make::ext::expr_unit()); + make::match_arm_list([arm.clone(), arm]) }; assert_eq!( arm_list.syntax().to_string(), diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 812b0dc888..9d84d060c3 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -63,6 +63,9 @@ pub mod ext { Some(expr) } + pub fn expr_unit() -> ast::Expr { + expr_tuple([]).into() + } pub fn expr_unreachable() -> ast::Expr { expr_from_text("unreachable!()") } @@ -546,10 +549,6 @@ pub fn hacky_block_expr( ast_from_text(&format!("fn f() {buf}")) } -pub fn expr_unit() -> ast::Expr { - expr_from_text("()") -} - pub fn expr_literal(text: &str) -> ast::Literal { assert_eq!(text.trim(), text); ast_from_text(&format!("fn f() {{ let _ = {text}; }}")) diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index 81c52e26cf..95162ea8d6 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs @@ -550,7 +550,7 @@ mod tests { None, None, make::param_list(None, []), - make::block_expr([], Some(make::expr_unit())), + make::block_expr([], Some(make::ext::expr_unit())), Some(make::ret_type(make::ty_unit())), false, false, From 32ff06d51ca1f24fcb82816726105d57b6ba31c9 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:28:17 -0500 Subject: [PATCH 07/98] internal: Generally improve `make::match_arm` `make::match_arm` should take a single `ast::Pat`, and callers can handle creating an `ast::OrPat` if need be. It should also take a proper `ast::MatchGuard`, instead of making one itself. --- .../src/handlers/add_missing_match_arms.rs | 12 +++----- .../src/handlers/extract_function.rs | 12 +++----- .../src/handlers/replace_if_let_with_match.rs | 12 ++++---- .../handlers/replace_try_expr_with_match.rs | 6 ++-- .../src/handlers/unmerge_match_arm.rs | 10 ++----- .../src/utils/gen_trait_fn_body.rs | 22 +++++++------- crates/syntax/src/ast/edit.rs | 3 +- crates/syntax/src/ast/make.rs | 30 ++++++++++++++----- crates/syntax/src/syntax_editor.rs | 2 +- 9 files changed, 53 insertions(+), 56 deletions(-) diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 24b34f140b..5899ec5a00 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -212,8 +212,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) !hidden }) .map(|(pat, _)| { - make::match_arm(iter::once(pat), None, make::ext::expr_todo()) - .clone_for_update() + make::match_arm(pat, None, make::ext::expr_todo()).clone_for_update() }); let catch_all_arm = new_match_arm_list @@ -243,12 +242,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) if needs_catch_all_arm && !has_catch_all_arm { cov_mark::hit!(added_wildcard_pattern); - let arm = make::match_arm( - iter::once(make::wildcard_pat().into()), - None, - make::ext::expr_todo(), - ) - .clone_for_update(); + let arm = + make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_todo()) + .clone_for_update(); todo_placeholders.push(arm.expr().unwrap()); added_arms.push(arm); } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 8273ebe310..abe4329bfe 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1554,12 +1554,12 @@ impl FlowHandler { let value_pat = make::ext::simple_ident_pat(make::name(some_name)); let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); let value = make::expr_path(make::ext::ident_path(some_name)); - make::match_arm(iter::once(pat.into()), None, value) + make::match_arm(pat.into(), None, value) }; let none_arm = { let path = make::ext::ident_path("None"); let pat = make::path_pat(path); - make::match_arm(iter::once(pat), None, none.make_result_handler(None)) + make::match_arm(pat, None, none.make_result_handler(None)) }; let arms = make::match_arm_list(vec![some_arm, none_arm]); make::expr_match(call_expr, arms) @@ -1573,18 +1573,14 @@ impl FlowHandler { let value_pat = make::ext::simple_ident_pat(make::name(ok_name)); let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); let value = make::expr_path(make::ext::ident_path(ok_name)); - make::match_arm(iter::once(pat.into()), None, value) + make::match_arm(pat.into(), None, value) }; let err_arm = { let path = make::ext::ident_path("Err"); let value_pat = make::ext::simple_ident_pat(make::name(err_name)); let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); let value = make::expr_path(make::ext::ident_path(err_name)); - make::match_arm( - iter::once(pat.into()), - None, - err.make_result_handler(Some(value)), - ) + make::match_arm(pat.into(), None, err.make_result_handler(Some(value))) }; let arms = make::match_arm_list(vec![ok_arm, err_arm]); make::expr_match(call_expr, arms) diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 1a1da6564a..95c7db2662 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -114,17 +114,15 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' let make_match_arm = |(pat, body): (_, ast::BlockExpr)| { let body = body.reset_indent().indent(IndentLevel(1)); match pat { - Either::Left(pat) => { - make::match_arm(iter::once(pat), None, unwrap_trivial_block(body)) - } + Either::Left(pat) => make::match_arm(pat, None, unwrap_trivial_block(body)), Either::Right(_) if !pat_seen => make::match_arm( - iter::once(make::literal_pat("true").into()), + make::literal_pat("true").into(), None, unwrap_trivial_block(body), ), Either::Right(expr) => make::match_arm( - iter::once(make::wildcard_pat().into()), - Some(expr), + make::wildcard_pat().into(), + Some(make::match_guard(expr)), unwrap_trivial_block(body), ), } @@ -181,7 +179,7 @@ fn make_else_arm( }; (pattern, make::ext::expr_unit()) }; - make::match_arm(iter::once(pattern), None, expr) + make::match_arm(pattern, None, expr) } // Assist: replace_match_with_if_let diff --git a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs index 2e26f59d03..aff5ee2ffc 100644 --- a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs @@ -71,13 +71,11 @@ pub(crate) fn replace_try_expr_with_match( }; let happy_arm = make::match_arm( - iter::once( - try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()), - ), + try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()), None, make::expr_path(make::ext::ident_path("it")), ); - let sad_arm = make::match_arm(iter::once(sad_pat), None, sad_expr); + let sad_arm = make::match_arm(sad_pat, None, sad_expr); let match_arm_list = make::match_arm_list([happy_arm, sad_arm]); diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs index c6cffb5434..6b9f661d4d 100644 --- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs @@ -54,13 +54,9 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let pats_after = pipe_token .siblings_with_tokens(Direction::Next) .filter_map(|it| ast::Pat::cast(it.into_node()?)); - // FIXME: We should add a leading pipe if the original arm has one. - let new_match_arm = make::match_arm( - pats_after, - match_arm.guard().and_then(|guard| guard.condition()), - match_arm_body, - ) - .clone_for_update(); + let new_pat = make::or_pat(pats_after, or_pat.leading_pipe().is_some()); + let new_match_arm = + make::match_arm(new_pat, match_arm.guard(), match_arm_body).clone_for_update(); let mut pipe_index = pipe_token.index(); if pipe_token diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 4ba6bd98e4..325e938240 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -66,7 +66,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let pat = make::record_pat(variant_name.clone(), pats.into_iter()); let fields = make::record_expr_field_list(fields); let record_expr = make::record_expr(variant_name, fields).into(); - arms.push(make::match_arm(Some(pat.into()), None, record_expr)); + arms.push(make::match_arm(pat.into(), None, record_expr)); } // => match self { Self::Name(arg1) => Self::Name(arg1.clone()) } @@ -84,14 +84,14 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter()); let struct_name = make::expr_path(variant_name); let tuple_expr = make::expr_call(struct_name, make::arg_list(fields)); - arms.push(make::match_arm(Some(pat.into()), None, tuple_expr)); + arms.push(make::match_arm(pat.into(), None, tuple_expr)); } // => match self { Self::Name => Self::Name } None => { let pattern = make::path_pat(variant_name.clone()); let variant_expr = make::expr_path(variant_name); - arms.push(make::match_arm(Some(pattern), None, variant_expr)); + arms.push(make::match_arm(pattern, None, variant_expr)); } } } @@ -190,7 +190,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(), let pat = make::record_pat(variant_name.clone(), pats.into_iter()); - arms.push(make::match_arm(Some(pat.into()), None, expr)); + arms.push(make::match_arm(pat.into(), None, expr)); } Some(ast::FieldList::TupleFieldList(list)) => { // => f.debug_tuple(name) @@ -223,7 +223,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(), let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter()); - arms.push(make::match_arm(Some(pat.into()), None, expr)); + arms.push(make::match_arm(pat.into(), None, expr)); } None => { let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into(); @@ -232,7 +232,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let macro_call = make::expr_macro_call(macro_name, args); let variant_name = make::path_pat(variant_name); - arms.push(make::match_arm(Some(variant_name), None, macro_call)); + arms.push(make::match_arm(variant_name, None, macro_call)); } } } @@ -485,7 +485,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) - let tuple = make::tuple_pat(vec![left.into(), right.into()]); if let Some(expr) = expr { - arms.push(make::match_arm(Some(tuple.into()), None, expr)); + arms.push(make::match_arm(tuple.into(), None, expr)); } } @@ -518,7 +518,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) - let tuple = make::tuple_pat(vec![left.into(), right.into()]); if let Some(expr) = expr { - arms.push(make::match_arm(Some(tuple.into()), None, expr)); + arms.push(make::match_arm(tuple.into(), None, expr)); } } None => continue, @@ -538,7 +538,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) - } else { eq_check }; - arms.push(make::match_arm(Some(lhs), None, rhs)); + arms.push(make::match_arm(lhs, None, rhs)); } let match_target = make::expr_tuple([lhs_name, rhs_name]).into(); @@ -599,10 +599,10 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) let variant_name = make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?); let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]); - arms.push(make::match_arm(Some(lhs.into()), None, make::expr_empty_block())); + arms.push(make::match_arm(lhs.into(), None, make::expr_empty_block())); arms.push(make::match_arm( - [make::ident_pat(false, false, make::name("ord")).into()], + make::ident_pat(false, false, make::name("ord")).into(), None, make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))), )); diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 46fdfb65b3..579f3ba8b4 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -153,8 +153,7 @@ impl AstNodeEdit for N {} #[test] fn test_increase_indent() { let arm_list = { - let arm = - make::match_arm(iter::once(make::wildcard_pat().into()), None, make::ext::expr_unit()); + let arm = make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_unit()); make::match_arm_list([arm.clone(), arm]) }; assert_eq!( diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 9d84d060c3..ca66a8744c 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -787,15 +787,21 @@ pub fn path_pat(path: ast::Path) -> ast::Pat { } } -pub fn match_arm( - pats: impl IntoIterator, - guard: Option, - expr: ast::Expr, -) -> ast::MatchArm { - let pats_str = pats.into_iter().join(" | "); +/// Returns a `Pat` if the path has just one segment, an `OrPat` otherwise. +pub fn or_pat(pats: impl IntoIterator, leading_pipe: bool) -> ast::Pat { + let leading_pipe = if leading_pipe { "| " } else { "" }; + let pats = pats.into_iter().join(" | "); + + return from_text(&format!("{leading_pipe}{pats}")); + fn from_text(text: &str) -> ast::Pat { + ast_from_text(&format!("fn f({text}: ())")) + } +} + +pub fn match_arm(pat: ast::Pat, guard: Option, expr: ast::Expr) -> ast::MatchArm { return match guard { - Some(guard) => from_text(&format!("{pats_str} if {guard} => {expr}")), - None => from_text(&format!("{pats_str} => {expr}")), + Some(guard) => from_text(&format!("{pat} {guard} => {expr}")), + None => from_text(&format!("{pat} => {expr}")), }; fn from_text(text: &str) -> ast::MatchArm { @@ -816,6 +822,14 @@ pub fn match_arm_with_guard( } } +pub fn match_guard(condition: ast::Expr) -> ast::MatchGuard { + return from_text(&format!("if {condition}")); + + fn from_text(text: &str) -> ast::MatchGuard { + ast_from_text(&format!("fn f() {{ match () {{() {text} => () }}")) + } +} + pub fn match_arm_list(arms: impl IntoIterator) -> ast::MatchArmList { let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| { let needs_comma = arm.expr().is_none_or(|it| !it.is_block_like()); diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index 95162ea8d6..b82181ae13 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs @@ -335,7 +335,7 @@ mod tests { #[test] fn basic_usage() { let root = make::match_arm( - [make::wildcard_pat().into()], + make::wildcard_pat().into(), None, make::expr_tuple([ make::expr_bin_op( From 551f1d00dd678df811c4ef67a65605be6e23bc81 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:42:42 -0500 Subject: [PATCH 08/98] internal: `make::expr_match` should return `ast::MatchExpr` --- crates/ide-assists/src/handlers/extract_function.rs | 4 ++-- .../ide-assists/src/handlers/replace_if_let_with_match.rs | 2 +- .../src/handlers/replace_try_expr_with_match.rs | 2 +- crates/ide-assists/src/utils/gen_trait_fn_body.rs | 8 ++++---- crates/syntax/src/ast/make.rs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index abe4329bfe..967da41c15 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1562,7 +1562,7 @@ impl FlowHandler { make::match_arm(pat, None, none.make_result_handler(None)) }; let arms = make::match_arm_list(vec![some_arm, none_arm]); - make::expr_match(call_expr, arms) + make::expr_match(call_expr, arms).into() } FlowHandler::MatchResult { err } => { let ok_name = "value"; @@ -1583,7 +1583,7 @@ impl FlowHandler { make::match_arm(pat.into(), None, err.make_result_handler(Some(value))) }; let arms = make::match_arm_list(vec![ok_arm, err_arm]); - make::expr_match(call_expr, arms) + make::expr_match(call_expr, arms).into() } } } diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 95c7db2662..1964a4c096 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -129,7 +129,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' }; let arms = cond_bodies.into_iter().map(make_match_arm).chain(iter::once(else_arm)); let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms)); - match_expr.indent(IndentLevel::from_node(if_expr.syntax())) + match_expr.indent(IndentLevel::from_node(if_expr.syntax())).into() }; let has_preceding_if_expr = diff --git a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs index aff5ee2ffc..88b50543dd 100644 --- a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs @@ -81,7 +81,7 @@ pub(crate) fn replace_try_expr_with_match( let expr_match = make::expr_match(expr, match_arm_list) .indent(IndentLevel::from_node(qm_kw_parent.syntax())); - edit.replace_ast::(qm_kw_parent.into(), expr_match); + edit.replace_ast::(qm_kw_parent.into(), expr_match.into()); }, ) } diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 325e938240..a9a889acc2 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -98,7 +98,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let match_target = make::expr_path(make::ext::ident_path("self")); let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - make::expr_match(match_target, list) + make::expr_match(match_target, list).into() } ast::Adt::Struct(strukt) => { match strukt.field_list() { @@ -241,7 +241,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); let match_expr = make::expr_match(match_target, list); - let body = make::block_expr(None, Some(match_expr)); + let body = make::block_expr(None, Some(match_expr.into())); let body = body.indent(ast::edit::IndentLevel(1)); ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); Some(()) @@ -543,7 +543,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) - let match_target = make::expr_tuple([lhs_name, rhs_name]).into(); let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - make::expr_match(match_target, list) + make::expr_match(match_target, list).into() } }; @@ -607,7 +607,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))), )); let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - Some(make::expr_stmt(make::expr_match(match_target, list)).into()) + Some(make::expr_stmt(make::expr_match(match_target, list).into()).into()) } fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index ca66a8744c..a920fe4f35 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -599,7 +599,7 @@ pub fn expr_try(expr: ast::Expr) -> ast::Expr { pub fn expr_await(expr: ast::Expr) -> ast::Expr { expr_from_text(&format!("{expr}.await")) } -pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr { +pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr { expr_from_text(&format!("match {expr} {match_arm_list}")) } pub fn expr_if( From f5ff966dea7ea0f5527a01f88e9c947983ba9cb9 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Sat, 14 Dec 2024 15:55:27 -0500 Subject: [PATCH 09/98] internal: Add some path constructors to `SyntaxFactory` --- .../src/ast/syntax_factory/constructors.rs | 78 ++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index bea6bfeafc..253791bfc0 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1,6 +1,6 @@ //! Wrappers over [`make`] constructors use crate::{ - ast::{self, make, HasGenericParams, HasName, HasTypeBounds, HasVisibility}, + ast::{self, make, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, HasVisibility}, syntax_editor::SyntaxMappingBuilder, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, }; @@ -12,6 +12,10 @@ impl SyntaxFactory { make::name(name).clone_for_update() } + pub fn name_ref(&self, name: &str) -> ast::NameRef { + make::name_ref(name).clone_for_update() + } + pub fn ty(&self, text: &str) -> ast::Type { make::ty(text).clone_for_update() } @@ -46,6 +50,71 @@ impl SyntaxFactory { ast } + pub fn path_segment(&self, name_ref: ast::NameRef) -> ast::PathSegment { + let ast = make::path_segment(name_ref.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn path_segment_generics( + &self, + name_ref: ast::NameRef, + generic_arg_list: ast::GenericArgList, + ) -> ast::PathSegment { + let ast::Type::PathType(path) = make::ty(&format!("{name_ref}{generic_arg_list}")) else { + unreachable!(); + }; + + let ast = path.path().unwrap().segment().unwrap().clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone()); + builder.map_node( + generic_arg_list.syntax().clone(), + ast.generic_arg_list().unwrap().syntax().clone(), + ); + builder.finish(&mut mapping); + } + + ast + } + + pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path { + let ast = make::path_unqualified(segment.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(segment.syntax().clone(), ast.segment().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn path_from_segments( + &self, + segments: impl IntoIterator, + is_abs: bool, + ) -> ast::Path { + let (segments, input) = iterator_input(segments); + let ast = make::path_from_segments(segments, is_abs).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input.into_iter(), ast.segments().map(|it| it.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat { let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update(); @@ -508,6 +577,13 @@ impl SyntaxFactory { } } +// `ext` constructors +impl SyntaxFactory { + pub fn ident_path(&self, ident: &str) -> ast::Path { + self.path_unqualified(self.path_segment(self.name_ref(ident))) + } +} + // We need to collect `input` here instead of taking `impl IntoIterator + Clone`, // because if we took `impl IntoIterator + Clone`, that could be something like an // `Iterator::map` with a closure that also makes use of a `SyntaxFactory` constructor. From ce2398fee85adfffc364334e3b9708941f7b246a Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:33:35 -0500 Subject: [PATCH 10/98] internal: Add some expr constructors to `SyntaxFactory` --- crates/syntax/src/ast/expr_ext.rs | 23 ++- .../src/ast/syntax_factory/constructors.rs | 154 +++++++++++++++++- 2 files changed, 175 insertions(+), 2 deletions(-) diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index 9466755576..93faeb40c3 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs @@ -10,7 +10,7 @@ use crate::{ FormatArgsArg, FormatArgsExpr, MacroDef, Static, TokenTree, }, AstToken, - SyntaxKind::*, + SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, T, }; @@ -50,6 +50,27 @@ impl From for ElseBranch { } } +impl AstNode for ElseBranch { + fn can_cast(kind: SyntaxKind) -> bool { + ast::BlockExpr::can_cast(kind) || ast::IfExpr::can_cast(kind) + } + + fn cast(syntax: SyntaxNode) -> Option { + if let Some(block_expr) = ast::BlockExpr::cast(syntax.clone()) { + Some(Self::Block(block_expr)) + } else { + ast::IfExpr::cast(syntax).map(Self::IfExpr) + } + } + + fn syntax(&self) -> &SyntaxNode { + match self { + ElseBranch::Block(block_expr) => block_expr.syntax(), + ElseBranch::IfExpr(if_expr) => if_expr.syntax(), + } + } +} + impl ast::IfExpr { pub fn condition(&self) -> Option { // If the condition is a BlockExpr, check if the then body is missing. diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 253791bfc0..39320cf504 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -167,6 +167,19 @@ impl SyntaxFactory { ast::BlockExpr { syntax: make::expr_empty_block().syntax().clone_for_update() } } + pub fn expr_tuple(&self, fields: impl IntoIterator) -> ast::TupleExpr { + let (fields, input) = iterator_input(fields); + let ast = make::expr_tuple(fields).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr { let ast::Expr::BinExpr(ast) = make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update() @@ -184,6 +197,10 @@ impl SyntaxFactory { ast } + pub fn expr_literal(&self, text: &str) -> ast::Literal { + make::expr_literal(text).clone_for_update() + } + pub fn expr_path(&self, path: ast::Path) -> ast::Expr { let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()).clone_for_update() else { unreachable!() @@ -198,6 +215,18 @@ impl SyntaxFactory { ast.into() } + pub fn expr_prefix(&self, op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr { + let ast = make::expr_prefix(op, expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr { let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update() else { @@ -229,6 +258,125 @@ impl SyntaxFactory { ast } + pub fn expr_if( + &self, + condition: ast::Expr, + then_branch: ast::BlockExpr, + else_branch: Option, + ) -> ast::IfExpr { + let ast = make::expr_if(condition.clone(), then_branch.clone(), else_branch.clone()) + .clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone()); + builder.map_node( + then_branch.syntax().clone(), + ast.then_branch().unwrap().syntax().clone(), + ); + + if let Some(else_branch) = else_branch { + builder.map_node( + else_branch.syntax().clone(), + ast.else_branch().unwrap().syntax().clone(), + ); + } + builder.finish(&mut mapping); + } + + ast + } + + pub fn expr_let(&self, pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { + let ast = make::expr_let(pattern.clone(), expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn expr_stmt(&self, expr: ast::Expr) -> ast::ExprStmt { + let ast = make::expr_stmt(expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn expr_match(&self, expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr { + let ast = make::expr_match(expr.clone(), match_arm_list.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.map_node( + match_arm_list.syntax().clone(), + ast.match_arm_list().unwrap().syntax().clone(), + ); + builder.finish(&mut mapping); + } + + ast + } + + pub fn match_arm( + &self, + pat: ast::Pat, + guard: Option, + expr: ast::Expr, + ) -> ast::MatchArm { + let ast = make::match_arm(pat.clone(), guard.clone(), expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone()); + if let Some(guard) = guard { + builder.map_node(guard.syntax().clone(), ast.guard().unwrap().syntax().clone()); + } + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn match_guard(&self, condition: ast::Expr) -> ast::MatchGuard { + let ast = make::match_guard(condition.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn match_arm_list( + &self, + match_arms: impl IntoIterator, + ) -> ast::MatchArmList { + let (match_arms, input) = iterator_input(match_arms); + let ast = make::match_arm_list(match_arms).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input.into_iter(), ast.arms().map(|it| it.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn let_stmt( &self, pattern: ast::Pat, @@ -572,13 +720,17 @@ impl SyntaxFactory { make::token(kind) } - pub fn whitespace(&self, text: &str) -> ast::SyntaxToken { + pub fn whitespace(&self, text: &str) -> SyntaxToken { make::tokens::whitespace(text) } } // `ext` constructors impl SyntaxFactory { + pub fn expr_unit(&self) -> ast::Expr { + self.expr_tuple([]).into() + } + pub fn ident_path(&self, ident: &str) -> ast::Path { self.path_unqualified(self.path_segment(self.name_ref(ident))) } From 796041acce7fa2d054456859f5eeb1b3d5166215 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:34:31 -0500 Subject: [PATCH 11/98] internal: Add some pattern constructors to `SyntaxFactory` --- .../src/ast/syntax_factory/constructors.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 39320cf504..d62c01ba76 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -127,6 +127,32 @@ impl SyntaxFactory { ast } + pub fn wildcard_pat(&self) -> ast::WildcardPat { + make::wildcard_pat().clone_for_update() + } + + pub fn literal_pat(&self, text: &str) -> ast::LiteralPat { + make::literal_pat(text).clone_for_update() + } + + pub fn tuple_struct_pat( + &self, + path: ast::Path, + fields: impl IntoIterator, + ) -> ast::TupleStructPat { + let (fields, input) = iterator_input(fields); + let ast = make::tuple_struct_pat(path.clone(), fields).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone()); + builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn block_expr( &self, statements: impl IntoIterator, From eb2ce57a3eeefc9dd6b47d64abaae7710a70ab16 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:39:43 -0500 Subject: [PATCH 12/98] internal: Migrate `replace_let_with_if_let` assist to `SyntaxEditor` --- .../src/handlers/replace_let_with_if_let.rs | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index 7dbb1f6e24..c071d3022d 100644 --- a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -1,12 +1,6 @@ -use std::iter::once; - use ide_db::ty_filter::TryEnum; use syntax::{ - ast::{ - self, - edit::{AstNodeEdit, IndentLevel}, - make, - }, + ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory}, AstNode, T, }; @@ -47,7 +41,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> AssistId("replace_let_with_if_let", AssistKind::RefactorRewrite), "Replace let with if let", target, - |edit| { + |builder| { + let mut editor = builder.make_editor(let_stmt.syntax()); + let make = SyntaxFactory::new(); let ty = ctx.sema.type_of_expr(&init); let happy_variant = ty .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted())) @@ -55,17 +51,18 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> let pat = match happy_variant { None => original_pat, Some(var_name) => { - make::tuple_struct_pat(make::ext::ident_path(var_name), once(original_pat)) - .into() + make.tuple_struct_pat(make.ident_path(var_name), [original_pat]).into() } }; - let block = - make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax())); - let if_ = make::expr_if(make::expr_let(pat, init).into(), block, None); - let stmt = make::expr_stmt(if_.into()); + let block = make.block_expr([], None); + block.indent(IndentLevel::from_node(let_stmt.syntax())); + let if_expr = make.expr_if(make.expr_let(pat, init).into(), block, None); + let if_stmt = make.expr_stmt(if_expr.into()); - edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); + editor.replace(let_stmt.syntax(), if_stmt.syntax()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.file_id(), editor); }, ) } From 54d9b5a31ae1f737a6209c274c61051a1a2b8090 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:52:14 -0500 Subject: [PATCH 13/98] internal: Migrate `if let` <=> `match` assists to `SyntaxEditor` --- .../src/handlers/replace_if_let_with_match.rs | 98 ++++++++++--------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 1964a4c096..e324d6eaaa 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -1,4 +1,4 @@ -use std::iter::{self, successors}; +use std::iter::successors; use either::Either; use ide_db::{ @@ -8,11 +8,7 @@ use ide_db::{ RootDatabase, }; use syntax::{ - ast::{ - self, - edit::{AstNodeEdit, IndentLevel}, - make, HasName, - }, + ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory, HasName}, AstNode, TextRange, T, }; @@ -108,51 +104,58 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite), format!("Replace if{let_} with match"), available_range, - move |edit| { + move |builder| { + let make = SyntaxFactory::new(); let match_expr = { - let else_arm = make_else_arm(ctx, else_block, &cond_bodies); + let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies); let make_match_arm = |(pat, body): (_, ast::BlockExpr)| { - let body = body.reset_indent().indent(IndentLevel(1)); + let body = make.block_expr(body.statements(), body.tail_expr()); + body.indent(IndentLevel::from(1)); + let body = unwrap_trivial_block(body); match pat { - Either::Left(pat) => make::match_arm(pat, None, unwrap_trivial_block(body)), - Either::Right(_) if !pat_seen => make::match_arm( - make::literal_pat("true").into(), - None, - unwrap_trivial_block(body), - ), - Either::Right(expr) => make::match_arm( - make::wildcard_pat().into(), - Some(make::match_guard(expr)), - unwrap_trivial_block(body), + Either::Left(pat) => make.match_arm(pat, None, body), + Either::Right(_) if !pat_seen => { + make.match_arm(make.literal_pat("true").into(), None, body) + } + Either::Right(expr) => make.match_arm( + make.wildcard_pat().into(), + Some(make.match_guard(expr)), + body, ), } }; - let arms = cond_bodies.into_iter().map(make_match_arm).chain(iter::once(else_arm)); - let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms)); - match_expr.indent(IndentLevel::from_node(if_expr.syntax())).into() + let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]); + let match_expr = make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms)); + match_expr.indent(IndentLevel::from_node(if_expr.syntax())); + match_expr.into() }; let has_preceding_if_expr = if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind())); let expr = if has_preceding_if_expr { // make sure we replace the `else if let ...` with a block so we don't end up with `else expr` - make::block_expr(None, Some(match_expr)).into() + make.block_expr([], Some(match_expr)).into() } else { match_expr }; - edit.replace_ast::(if_expr.into(), expr); + + let mut editor = builder.make_editor(if_expr.syntax()); + editor.replace(if_expr.syntax(), expr.syntax()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.file_id(), editor); }, ) } fn make_else_arm( ctx: &AssistContext<'_>, + make: &SyntaxFactory, else_block: Option, conditionals: &[(Either, ast::BlockExpr)], ) -> ast::MatchArm { let (pattern, expr) = if let Some(else_block) = else_block { let pattern = match conditionals { - [(Either::Right(_), _)] => make::literal_pat("false").into(), + [(Either::Right(_), _)] => make.literal_pat("false").into(), [(Either::Left(pat), _)] => match ctx .sema .type_of_pat(pat) @@ -162,24 +165,24 @@ fn make_else_arm( if does_pat_match_variant(pat, &it.sad_pattern()) { it.happy_pattern_wildcard() } else if does_pat_variant_nested_or_literal(ctx, pat) { - make::wildcard_pat().into() + make.wildcard_pat().into() } else { it.sad_pattern() } } - None => make::wildcard_pat().into(), + None => make.wildcard_pat().into(), }, - _ => make::wildcard_pat().into(), + _ => make.wildcard_pat().into(), }; (pattern, unwrap_trivial_block(else_block)) } else { let pattern = match conditionals { - [(Either::Right(_), _)] => make::literal_pat("false").into(), - _ => make::wildcard_pat().into(), + [(Either::Right(_), _)] => make.literal_pat("false").into(), + _ => make.wildcard_pat().into(), }; - (pattern, make::ext::expr_unit()) + (pattern, make.expr_unit()) }; - make::match_arm(pattern, None, expr) + make.match_arm(pattern, None, expr) } // Assist: replace_match_with_if_let @@ -245,21 +248,21 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' } _ => " let", }; - let target = match_expr.syntax().text_range(); acc.add( AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite), format!("Replace match with if{let_}"), - target, - move |edit| { - fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr { + match_expr.syntax().text_range(), + move |builder| { + let make = SyntaxFactory::new(); + let make_block_expr = |expr: ast::Expr| { // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are // formatted without enclosing braces. If we encounter such block exprs, // wrap them in another BlockExpr. match expr { ast::Expr::BlockExpr(block) if block.modifier().is_none() => block, - expr => make::block_expr(iter::empty(), Some(expr)), + expr => make.block_expr([], Some(expr)), } - } + }; let condition = match if_let_pat { ast::Pat::LiteralPat(p) @@ -270,20 +273,25 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' ast::Pat::LiteralPat(p) if p.literal().is_some_and(|it| it.token().kind() == T![false]) => { - make::expr_prefix(T![!], scrutinee).into() + make.expr_prefix(T![!], scrutinee).into() } - _ => make::expr_let(if_let_pat, scrutinee).into(), + _ => make.expr_let(if_let_pat, scrutinee).into(), }; - let then_block = make_block_expr(then_expr.reset_indent()); + let then_expr = then_expr.clone_for_update(); + then_expr.reindent_to(IndentLevel::single()); + let then_block = make_block_expr(then_expr); let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) }; - let if_let_expr = make::expr_if( + let if_let_expr = make.expr_if( condition, then_block, else_expr.map(make_block_expr).map(ast::ElseBranch::Block), - ) - .indent(IndentLevel::from_node(match_expr.syntax())); + ); + if_let_expr.indent(IndentLevel::from_node(match_expr.syntax())); - edit.replace_ast::(match_expr.into(), if_let_expr.into()); + let mut editor = builder.make_editor(match_expr.syntax()); + editor.replace(match_expr.syntax(), if_let_expr.syntax()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.file_id(), editor); }, ) } From 0d4af7bce317a11a16ec4d63660554ef768b4a26 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 7 Jan 2025 23:04:53 +0200 Subject: [PATCH 14/98] Fix a bug with missing binding in MBE We should immediately mark them as finished, on the first entry. The funny (or sad) part was that this bug was pre-existing, but previously to #18327, it was causing us to generate bindings non-stop, 65535 of them, until we get to the hardcoded repetition limit, and then throw it all away. And it was so Blazingly Fast that nobody noticed. With #18327 however, this is still what happens, except that now instead of *merging* the fragments into the result, we write them on-demand. Meaning that when we hit the limit, we've already written all previous entries. This is a minor change, I thought for myself when I was writing this, and it's actually for the better, so who cares. Minor change? Not so fast. This caused us to emit 65535 repetitions, all of which the MBE infra needs to handle when calling other macros with the expansion, and convert to rowan tree etc., which resulted a *massive* hang. The test (and also `analysis-stats`) used to crash with stack overflow on this macro, because we were dropping some crazily deep rowan tree. Now they work properly. Because I am lazy, and also because I could not find the exact conditions that causes a macro match but with a missing binding, I just copied all macros from tracing. Easy. --- crates/ide-diagnostics/src/tests.rs | 2 + .../src/tests/overly_long_real_world_cases.rs | 2726 +++++++++++++++++ crates/mbe/src/expander/transcriber.rs | 5 +- 3 files changed, 2732 insertions(+), 1 deletion(-) create mode 100644 crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index ec74640a54..7cf51f3d14 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -1,5 +1,7 @@ #![allow(clippy::print_stderr)] +mod overly_long_real_world_cases; + use ide_db::{ assists::AssistResolveStrategy, base_db::SourceDatabase, LineIndexDatabase, RootDatabase, }; diff --git a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs new file mode 100644 index 0000000000..c6831d818a --- /dev/null +++ b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs @@ -0,0 +1,2726 @@ +//! Overly long excerpts of failures from real world cases, that I was too lazy to minimize. + +use crate::tests::check_diagnostics_with_disabled; + +#[test] +fn tracing_infinite_repeat() { + check_diagnostics_with_disabled( + r#" +//- /core.rs crate:core +#[rustc_builtin_macro] +#[macro_export] +macro_rules! concat { +($($e:expr),* $(,)?) => {{ /* compiler built-in */ }}; +} +#[rustc_builtin_macro] +#[macro_export] +macro_rules! file { +() => { + /* compiler built-in */ +}; +} +#[allow_internal_unsafe] +#[allow_internal_unstable(fmt_internals)] +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args { +($fmt:expr) => {{ /* compiler built-in */ }}; +($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; +} +#[rustc_builtin_macro] +#[macro_export] +macro_rules! line { +() => { + /* compiler built-in */ +}; +} + +//- /tracing_core.rs crate:tracing_core deps:core +#[macro_export] +macro_rules! identify_callsite { +($callsite:expr) => { + $crate::callsite::Identifier($callsite) +}; +} + +#[macro_export] +macro_rules! metadata { +( + name: $name:expr, + target: $target:expr, + level: $level:expr, + fields: $fields:expr, + callsite: $callsite:expr, + kind: $kind:expr +) => { + $crate::metadata! { + name: $name, + target: $target, + level: $level, + fields: $fields, + callsite: $callsite, + kind: $kind, + } +}; +( + name: $name:expr, + target: $target:expr, + level: $level:expr, + fields: $fields:expr, + callsite: $callsite:expr, + kind: $kind:expr, +) => { + $crate::metadata::Metadata::new( + $name, + $target, + $level, + $crate::__macro_support::Option::Some($crate::__macro_support::file!()), + $crate::__macro_support::Option::Some($crate::__macro_support::line!()), + $crate::__macro_support::Option::Some($crate::__macro_support::module_path!()), + $crate::field::FieldSet::new($fields, $crate::identify_callsite!($callsite)), + $kind, + ) +}; +} + +//- /tracing.rs crate:tracing deps:core,tracing_core +#[doc(hidden)] +pub mod __macro_support { +// Re-export the `core` functions that are used in macros. This allows +// a crate to be named `core` and avoid name clashes. +// See here: https://github.com/tokio-rs/tracing/issues/2761 +pub use core::{concat, file, format_args, iter::Iterator, line, option::Option}; +} + +#[macro_export] +macro_rules! span { +(target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr) => { + $crate::span!(target: $target, parent: $parent, $lvl, $name,) +}; +(target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { + { + use $crate::__macro_support::Callsite as _; + static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + name: $name, + kind: $crate::metadata::Kind::SPAN, + target: $target, + level: $lvl, + fields: $($fields)* + }; + let mut interest = $crate::subscriber::Interest::never(); + if $crate::level_enabled!($lvl) + && { interest = __CALLSITE.interest(); !interest.is_never() } + && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) + { + let meta = __CALLSITE.metadata(); + // span with explicit parent + $crate::Span::child_of( + $parent, + meta, + &$crate::valueset!(meta.fields(), $($fields)*), + ) + } else { + let span = $crate::__macro_support::__disabled_span(__CALLSITE.metadata()); + $crate::if_log_enabled! { $lvl, { + span.record_all(&$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)); + }}; + span + } + } +}; +(target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { + { + use $crate::__macro_support::Callsite as _; + static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! { + name: $name, + kind: $crate::metadata::Kind::SPAN, + target: $target, + level: $lvl, + fields: $($fields)* + }; + let mut interest = $crate::subscriber::Interest::never(); + if $crate::level_enabled!($lvl) + && { interest = __CALLSITE.interest(); !interest.is_never() } + && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) + { + let meta = __CALLSITE.metadata(); + // span with contextual parent + $crate::Span::new( + meta, + &$crate::valueset!(meta.fields(), $($fields)*), + ) + } else { + let span = $crate::__macro_support::__disabled_span(__CALLSITE.metadata()); + $crate::if_log_enabled! { $lvl, { + span.record_all(&$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)); + }}; + span + } + } +}; +(target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr) => { + $crate::span!(target: $target, parent: $parent, $lvl, $name,) +}; +(parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { + $crate::span!( + target: module_path!(), + parent: $parent, + $lvl, + $name, + $($fields)* + ) +}; +(parent: $parent:expr, $lvl:expr, $name:expr) => { + $crate::span!( + target: module_path!(), + parent: $parent, + $lvl, + $name, + ) +}; +(target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { + $crate::span!( + target: $target, + $lvl, + $name, + $($fields)* + ) +}; +(target: $target:expr, $lvl:expr, $name:expr) => { + $crate::span!(target: $target, $lvl, $name,) +}; +($lvl:expr, $name:expr, $($fields:tt)*) => { + $crate::span!( + target: module_path!(), + $lvl, + $name, + $($fields)* + ) +}; +($lvl:expr, $name:expr) => { + $crate::span!( + target: module_path!(), + $lvl, + $name, + ) +}; +} + +#[macro_export] +macro_rules! trace_span { +(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + parent: $parent, + $crate::Level::TRACE, + $name, + $($field)* + ) +}; +(target: $target:expr, parent: $parent:expr, $name:expr) => { + $crate::trace_span!(target: $target, parent: $parent, $name,) +}; +(parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + parent: $parent, + $crate::Level::TRACE, + $name, + $($field)* + ) +}; +(parent: $parent:expr, $name:expr) => { + $crate::trace_span!(parent: $parent, $name,) +}; +(target: $target:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + $crate::Level::TRACE, + $name, + $($field)* + ) +}; +(target: $target:expr, $name:expr) => { + $crate::trace_span!(target: $target, $name,) +}; +($name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + $crate::Level::TRACE, + $name, + $($field)* + ) +}; +($name:expr) => { $crate::trace_span!($name,) }; +} + +#[macro_export] +macro_rules! debug_span { +(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + parent: $parent, + $crate::Level::DEBUG, + $name, + $($field)* + ) +}; +(target: $target:expr, parent: $parent:expr, $name:expr) => { + $crate::debug_span!(target: $target, parent: $parent, $name,) +}; +(parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + parent: $parent, + $crate::Level::DEBUG, + $name, + $($field)* + ) +}; +(parent: $parent:expr, $name:expr) => { + $crate::debug_span!(parent: $parent, $name,) +}; +(target: $target:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + $crate::Level::DEBUG, + $name, + $($field)* + ) +}; +(target: $target:expr, $name:expr) => { + $crate::debug_span!(target: $target, $name,) +}; +($name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + $crate::Level::DEBUG, + $name, + $($field)* + ) +}; +($name:expr) => {$crate::debug_span!($name,)}; +} + +#[macro_export] +macro_rules! info_span { +(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + parent: $parent, + $crate::Level::INFO, + $name, + $($field)* + ) +}; +(target: $target:expr, parent: $parent:expr, $name:expr) => { + $crate::info_span!(target: $target, parent: $parent, $name,) +}; +(parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + parent: $parent, + $crate::Level::INFO, + $name, + $($field)* + ) +}; +(parent: $parent:expr, $name:expr) => { + $crate::info_span!(parent: $parent, $name,) +}; +(target: $target:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + $crate::Level::INFO, + $name, + $($field)* + ) +}; +(target: $target:expr, $name:expr) => { + $crate::info_span!(target: $target, $name,) +}; +($name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + $crate::Level::INFO, + $name, + $($field)* + ) +}; +($name:expr) => {$crate::info_span!($name,)}; +} + +#[macro_export] +macro_rules! warn_span { +(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + parent: $parent, + $crate::Level::WARN, + $name, + $($field)* + ) +}; +(target: $target:expr, parent: $parent:expr, $name:expr) => { + $crate::warn_span!(target: $target, parent: $parent, $name,) +}; +(parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + parent: $parent, + $crate::Level::WARN, + $name, + $($field)* + ) +}; +(parent: $parent:expr, $name:expr) => { + $crate::warn_span!(parent: $parent, $name,) +}; +(target: $target:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + $crate::Level::WARN, + $name, + $($field)* + ) +}; +(target: $target:expr, $name:expr) => { + $crate::warn_span!(target: $target, $name,) +}; +($name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + $crate::Level::WARN, + $name, + $($field)* + ) +}; +($name:expr) => {$crate::warn_span!($name,)}; +} + +#[macro_export] +macro_rules! error_span { +(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + parent: $parent, + $crate::Level::ERROR, + $name, + $($field)* + ) +}; +(target: $target:expr, parent: $parent:expr, $name:expr) => { + $crate::error_span!(target: $target, parent: $parent, $name,) +}; +(parent: $parent:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + parent: $parent, + $crate::Level::ERROR, + $name, + $($field)* + ) +}; +(parent: $parent:expr, $name:expr) => { + $crate::error_span!(parent: $parent, $name,) +}; +(target: $target:expr, $name:expr, $($field:tt)*) => { + $crate::span!( + target: $target, + $crate::Level::ERROR, + $name, + $($field)* + ) +}; +(target: $target:expr, $name:expr) => { + $crate::error_span!(target: $target, $name,) +}; +($name:expr, $($field:tt)*) => { + $crate::span!( + target: module_path!(), + $crate::Level::ERROR, + $name, + $($field)* + ) +}; +($name:expr) => {$crate::error_span!($name,)}; +} + +#[macro_export] +macro_rules! event { +// Name / target / parent. +(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({ + use $crate::__macro_support::Callsite as _; + static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + name: $name, + kind: $crate::metadata::Kind::EVENT, + target: $target, + level: $lvl, + fields: $($fields)* + }; + + let enabled = $crate::level_enabled!($lvl) && { + let interest = __CALLSITE.interest(); + !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) + }; + if enabled { + (|value_set: $crate::field::ValueSet| { + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &value_set + ); + let meta = __CALLSITE.metadata(); + // event with explicit parent + $crate::Event::child_of( + $parent, + meta, + &value_set + ); + })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)); + } else { + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*) + ); + } +}); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + name: $name, + target: $target, + parent: $parent, + $lvl, + { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* } + ) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $lvl, { $($k).+ = $($fields)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $lvl, { $($arg)+ }) +); + +// Name / target. +(name: $name:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({ + use $crate::__macro_support::Callsite as _; + static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + name: $name, + kind: $crate::metadata::Kind::EVENT, + target: $target, + level: $lvl, + fields: $($fields)* + }; + let enabled = $crate::level_enabled!($lvl) && { + let interest = __CALLSITE.interest(); + !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) + }; + if enabled { + (|value_set: $crate::field::ValueSet| { + let meta = __CALLSITE.metadata(); + // event with contextual parent + $crate::Event::dispatch( + meta, + &value_set + ); + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &value_set + ); + })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)); + } else { + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*) + ); + } +}); +(name: $name:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + name: $name, + target: $target, + $lvl, + { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* } + ) +); +(name: $name:expr, target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => ( + $crate::event!(name: $name, target: $target, $lvl, { $($k).+ = $($fields)* }) +); +(name: $name:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => ( + $crate::event!(name: $name, target: $target, $lvl, { $($arg)+ }) +); + +// Target / parent. +(target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({ + use $crate::__macro_support::Callsite as _; + static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! { + name: $crate::__macro_support::concat!( + "event ", + $crate::__macro_support::file!(), + ":", + $crate::__macro_support::line!() + ), + kind: $crate::metadata::Kind::EVENT, + target: $target, + level: $lvl, + fields: $($fields)* + }; + + let enabled = $crate::level_enabled!($lvl) && { + let interest = __CALLSITE.interest(); + !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) + }; + if enabled { + (|value_set: $crate::field::ValueSet| { + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &value_set + ); + let meta = __CALLSITE.metadata(); + // event with explicit parent + $crate::Event::child_of( + $parent, + meta, + &value_set + ); + })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)); + } else { + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*) + ); + } +}); +(target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + target: $target, + parent: $parent, + $lvl, + { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* } + ) +); +(target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $lvl, { $($k).+ = $($fields)* }) +); +(target: $target:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => ( + $crate::event!(target: $target, parent: $parent, $lvl, { $($arg)+ }) +); + +// Name / parent. +(name: $name:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({ + use $crate::__macro_support::Callsite as _; + static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + name: $name, + kind: $crate::metadata::Kind::EVENT, + target: module_path!(), + level: $lvl, + fields: $($fields)* + }; + + let enabled = $crate::level_enabled!($lvl) && { + let interest = __CALLSITE.interest(); + !interest.is_never() && __CALLSITE.is_enabled(interest) + }; + if enabled { + (|value_set: $crate::field::ValueSet| { + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &value_set + ); + let meta = __CALLSITE.metadata(); + // event with explicit parent + $crate::Event::child_of( + $parent, + meta, + &value_set + ); + })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)); + } else { + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*) + ); + } +}); +(name: $name:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + name: $name, + parent: $parent, + $lvl, + { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* } + ) +); +(name: $name:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $lvl, { $($k).+ = $($fields)* }) +); +(name: $name:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => ( + $crate::event!(name: $name, parent: $parent, $lvl, { $($arg)+ }) +); + +// Name. +(name: $name:expr, $lvl:expr, { $($fields:tt)* } )=> ({ + use $crate::__macro_support::Callsite as _; + static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + name: $name, + kind: $crate::metadata::Kind::EVENT, + target: module_path!(), + level: $lvl, + fields: $($fields)* + }; + let enabled = $crate::level_enabled!($lvl) && { + let interest = __CALLSITE.interest(); + !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) + }; + if enabled { + (|value_set: $crate::field::ValueSet| { + let meta = __CALLSITE.metadata(); + // event with contextual parent + $crate::Event::dispatch( + meta, + &value_set + ); + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &value_set + ); + })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)); + } else { + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*) + ); + } +}); +(name: $name:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + name: $name, + $lvl, + { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* } + ) +); +(name: $name:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => ( + $crate::event!(name: $name, $lvl, { $($k).+ = $($fields)* }) +); +(name: $name:expr, $lvl:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, $lvl, { $($arg)+ }) +); + +// Target. +(target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({ + use $crate::__macro_support::Callsite as _; + static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! { + name: $crate::__macro_support::concat!( + "event ", + $crate::__macro_support::file!(), + ":", + $crate::__macro_support::line!() + ), + kind: $crate::metadata::Kind::EVENT, + target: $target, + level: $lvl, + fields: $($fields)* + }; + let enabled = $crate::level_enabled!($lvl) && { + let interest = __CALLSITE.interest(); + !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) + }; + if enabled { + (|value_set: $crate::field::ValueSet| { + let meta = __CALLSITE.metadata(); + // event with contextual parent + $crate::Event::dispatch( + meta, + &value_set + ); + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &value_set + ); + })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)); + } else { + $crate::__tracing_log!( + $lvl, + __CALLSITE, + &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*) + ); + } +}); +(target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + target: $target, + $lvl, + { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* } + ) +); +(target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => ( + $crate::event!(target: $target, $lvl, { $($k).+ = $($fields)* }) +); +(target: $target:expr, $lvl:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $lvl, { $($arg)+ }) +); + +// Parent. +(parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $lvl, + { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* } + ) +); +(parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $lvl, + { $($k).+ = $($field)*} + ) +); +(parent: $parent:expr, $lvl:expr, ?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $lvl, + { ?$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, $lvl:expr, %$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $lvl, + { %$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, $lvl:expr, $($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $lvl, + { $($k).+, $($field)*} + ) +); +(parent: $parent:expr, $lvl:expr, %$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $lvl, + { %$($k).+, $($field)*} + ) +); +(parent: $parent:expr, $lvl:expr, ?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $lvl, + { ?$($k).+, $($field)*} + ) +); +(parent: $parent:expr, $lvl:expr, $($arg:tt)+ ) => ( + $crate::event!(target: module_path!(), parent: $parent, $lvl, { $($arg)+ }) +); + +// ... +( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $lvl, + { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* } + ) +); +( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $lvl, + { message = format_args!($($arg)+), $($fields)* } + ) +); +($lvl:expr, $($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $lvl, + { $($k).+ = $($field)*} + ) +); +($lvl:expr, $($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $lvl, + { $($k).+, $($field)*} + ) +); +($lvl:expr, ?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $lvl, + { ?$($k).+, $($field)*} + ) +); +($lvl:expr, %$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $lvl, + { %$($k).+, $($field)*} + ) +); +($lvl:expr, ?$($k:ident).+) => ( + $crate::event!($lvl, ?$($k).+,) +); +($lvl:expr, %$($k:ident).+) => ( + $crate::event!($lvl, %$($k).+,) +); +($lvl:expr, $($k:ident).+) => ( + $crate::event!($lvl, $($k).+,) +); +( $lvl:expr, $($arg:tt)+ ) => ( + $crate::event!(target: module_path!(), $lvl, { $($arg)+ }) +); +} + +#[macro_export] +macro_rules! event_enabled { +($($rest:tt)*)=> ( + $crate::enabled!(kind: $crate::metadata::Kind::EVENT, $($rest)*) +) +} + +#[macro_export] +macro_rules! span_enabled { +($($rest:tt)*)=> ( + $crate::enabled!(kind: $crate::metadata::Kind::SPAN, $($rest)*) +) +} + +#[macro_export] +macro_rules! enabled { +(kind: $kind:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({ + if $crate::level_enabled!($lvl) { + use $crate::__macro_support::Callsite as _; + static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! { + name: $crate::__macro_support::concat!( + "enabled ", + $crate::__macro_support::file!(), + ":", + $crate::__macro_support::line!() + ), + kind: $kind.hint(), + target: $target, + level: $lvl, + fields: $($fields)* + }; + let interest = __CALLSITE.interest(); + if !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) { + let meta = __CALLSITE.metadata(); + $crate::dispatcher::get_default(|current| current.enabled(meta)) + } else { + false + } + } else { + false + } +}); +// Just target and level +(kind: $kind:expr, target: $target:expr, $lvl:expr ) => ( + $crate::enabled!(kind: $kind, target: $target, $lvl, { }) +); +(target: $target:expr, $lvl:expr ) => ( + $crate::enabled!(kind: $crate::metadata::Kind::HINT, target: $target, $lvl, { }) +); + +// These four cases handle fields with no values +(kind: $kind:expr, target: $target:expr, $lvl:expr, $($field:tt)*) => ( + $crate::enabled!( + kind: $kind, + target: $target, + $lvl, + { $($field)*} + ) +); +(target: $target:expr, $lvl:expr, $($field:tt)*) => ( + $crate::enabled!( + kind: $crate::metadata::Kind::HINT, + target: $target, + $lvl, + { $($field)*} + ) +); + +// Level and field case +(kind: $kind:expr, $lvl:expr, $($field:tt)*) => ( + $crate::enabled!( + kind: $kind, + target: module_path!(), + $lvl, + { $($field)*} + ) +); + +// Simplest `enabled!` case +(kind: $kind:expr, $lvl:expr) => ( + $crate::enabled!(kind: $kind, target: module_path!(), $lvl, { }) +); +($lvl:expr) => ( + $crate::enabled!(kind: $crate::metadata::Kind::HINT, target: module_path!(), $lvl, { }) +); + +// Fallthrough from above +($lvl:expr, $($field:tt)*) => ( + $crate::enabled!( + kind: $crate::metadata::Kind::HINT, + target: module_path!(), + $lvl, + { $($field)*} + ) +); +} + +#[macro_export] +macro_rules! trace { +// Name / target / parent. +(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+) +); + +// Name / target. +(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::TRACE, {}, $($arg)+) +); + +// Target / parent. +(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*) +); +(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+) +); + +// Name / parent. +(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*) +); +(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, {}, $($arg)+) +); + +// Name. +(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::TRACE, { $($field)* }, $($arg)*) +); +(name: $name:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::TRACE, { $($k).+ $($field)* }) +); +(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::TRACE, { ?$($k).+ $($field)* }) +); +(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::TRACE, { %$($k).+ $($field)* }) +); +(name: $name:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, $crate::Level::TRACE, {}, $($arg)+) +); + +// Target. +(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*) +); +(target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, { $($k).+ $($field)* }) +); +(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* }) +); +(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* }) +); +(target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, {}, $($arg)+) +); + +// Parent. +(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::TRACE, + { $($field)+ }, + $($arg)+ + ) +); +(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::TRACE, + { $($k).+ = $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::TRACE, + { ?$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::TRACE, + { %$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::TRACE, + { $($k).+, $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::TRACE, + { ?$($k).+, $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::TRACE, + { %$($k).+, $($field)*} + ) +); +(parent: $parent:expr, $($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::TRACE, + {}, + $($arg)+ + ) +); + +// ... +({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { $($field)+ }, + $($arg)+ + ) +); +($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { $($k).+ = $($field)*} + ) +); +(?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { ?$($k).+ = $($field)*} + ) +); +(%$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { %$($k).+ = $($field)*} + ) +); +($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { $($k).+, $($field)*} + ) +); +(?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { ?$($k).+, $($field)*} + ) +); +(%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { %$($k).+, $($field)*} + ) +); +(?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { ?$($k).+ } + ) +); +(%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { %$($k).+ } + ) +); +($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { $($k).+ } + ) +); +($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + $($arg)+ + ) +); +} + +#[macro_export] +macro_rules! debug { +// Name / target / parent. +(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+) +); + +// Name / target. +(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, {}, $($arg)+) +); + +// Target / parent. +(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*) +); +(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+) +); + +// Name / parent. +(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*) +); +(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+) +); + +// Name. +(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::DEBUG, { $($field)* }, $($arg)*) +); +(name: $name:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::DEBUG, { $($k).+ $($field)* }) +); +(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::DEBUG, { ?$($k).+ $($field)* }) +); +(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::DEBUG, { %$($k).+ $($field)* }) +); +(name: $name:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, $crate::Level::DEBUG, {}, $($arg)+) +); + +// Target. +(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*) +); +(target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* }) +); +(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* }) +); +(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* }) +); +(target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, {}, $($arg)+) +); + +// Parent. +(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::DEBUG, + { $($field)+ }, + $($arg)+ + ) +); +(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::DEBUG, + { $($k).+ = $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::DEBUG, + { ?$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::DEBUG, + { %$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::DEBUG, + { $($k).+, $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::DEBUG, + { ?$($k).+, $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::DEBUG, + { %$($k).+, $($field)*} + ) +); +(parent: $parent:expr, $($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::DEBUG, + {}, + $($arg)+ + ) +); + +// ... +({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { $($field)+ }, + $($arg)+ + ) +); +($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { $($k).+ = $($field)*} + ) +); +(?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { ?$($k).+ = $($field)*} + ) +); +(%$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { %$($k).+ = $($field)*} + ) +); +($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { $($k).+, $($field)*} + ) +); +(?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { ?$($k).+, $($field)*} + ) +); +(%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { %$($k).+, $($field)*} + ) +); +(?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { ?$($k).+ } + ) +); +(%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { %$($k).+ } + ) +); +($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { $($k).+ } + ) +); +($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + $($arg)+ + ) +); +} + +#[macro_export] +macro_rules! info { +// Name / target / parent. +(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+) +); + +// Name / target. +(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::INFO, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::INFO, {}, $($arg)+) +); + +// Target / parent. +(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*) +); +(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+) +); + +// Name / parent. +(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*) +); +(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, {}, $($arg)+) +); + +// Name. +(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::INFO, { $($field)* }, $($arg)*) +); +(name: $name:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::INFO, { $($k).+ $($field)* }) +); +(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::INFO, { ?$($k).+ $($field)* }) +); +(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::INFO, { %$($k).+ $($field)* }) +); +(name: $name:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, $crate::Level::INFO, {}, $($arg)+) +); + +// Target. +(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*) +); +(target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::INFO, { $($k).+ $($field)* }) +); +(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* }) +); +(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::INFO, { %$($k).+ $($field)* }) +); +(target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::INFO, {}, $($arg)+) +); + +// Parent. +(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::INFO, + { $($field)+ }, + $($arg)+ + ) +); +(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::INFO, + { $($k).+ = $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::INFO, + { ?$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::INFO, + { %$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::INFO, + { $($k).+, $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::INFO, + { ?$($k).+, $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::INFO, + { %$($k).+, $($field)*} + ) +); +(parent: $parent:expr, $($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::INFO, + {}, + $($arg)+ + ) +); + +// ... +({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { $($field)+ }, + $($arg)+ + ) +); +($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { $($k).+ = $($field)*} + ) +); +(?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { ?$($k).+ = $($field)*} + ) +); +(%$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { %$($k).+ = $($field)*} + ) +); +($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { $($k).+, $($field)*} + ) +); +(?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { ?$($k).+, $($field)*} + ) +); +(%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { %$($k).+, $($field)*} + ) +); +(?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { ?$($k).+ } + ) +); +(%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { %$($k).+ } + ) +); +($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { $($k).+ } + ) +); +($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + $($arg)+ + ) +); +} + +#[macro_export] +macro_rules! warn { +// Name / target / parent. +(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+) +); + +// Name / target. +(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::WARN, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::WARN, {}, $($arg)+) +); + +// Target / parent. +(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*) +); +(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+) +); + +// Name / parent. +(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*) +); +(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, {}, $($arg)+) +); + +// Name. +(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::WARN, { $($field)* }, $($arg)*) +); +(name: $name:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::WARN, { $($k).+ $($field)* }) +); +(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::WARN, { ?$($k).+ $($field)* }) +); +(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::WARN, { %$($k).+ $($field)* }) +); +(name: $name:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, $crate::Level::WARN, {}, $($arg)+) +); + +// Target. +(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*) +); +(target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::WARN, { $($k).+ $($field)* }) +); +(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* }) +); +(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::WARN, { %$($k).+ $($field)* }) +); +(target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::WARN, {}, $($arg)+) +); + +// Parent. +(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::WARN, + { $($field)+ }, + $($arg)+ + ) +); +(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::WARN, + { $($k).+ = $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::WARN, + { ?$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::WARN, + { %$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::WARN, + { $($k).+, $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::WARN, + { ?$($k).+, $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::WARN, + { %$($k).+, $($field)*} + ) +); +(parent: $parent:expr, $($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::WARN, + {}, + $($arg)+ + ) +); + +// ... +({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { $($field)+ }, + $($arg)+ + ) +); +($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { $($k).+ = $($field)*} + ) +); +(?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { ?$($k).+ = $($field)*} + ) +); +(%$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { %$($k).+ = $($field)*} + ) +); +($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { $($k).+, $($field)*} + ) +); +(?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { ?$($k).+, $($field)*} + ) +); +(%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { %$($k).+, $($field)*} + ) +); +(?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { ?$($k).+ } + ) +); +(%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { %$($k).+ } + ) +); +($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { $($k).+ } + ) +); +($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + $($arg)+ + ) +); +} + +#[macro_export] +macro_rules! error { +// Name / target / parent. +(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+) +); + +// Name / target. +(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*) +); +(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* }) +); +(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, target: $target, $crate::Level::ERROR, {}, $($arg)+) +); + +// Target / parent. +(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*) +); +(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* }) +); +(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+) +); + +// Name / parent. +(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*) +); +(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* }) +); +(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, {}, $($arg)+) +); + +// Name. +(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::ERROR, { $($field)* }, $($arg)*) +); +(name: $name:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::ERROR, { $($k).+ $($field)* }) +); +(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::ERROR, { ?$($k).+ $($field)* }) +); +(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(name: $name, $crate::Level::ERROR, { %$($k).+ $($field)* }) +); +(name: $name:expr, $($arg:tt)+ ) => ( + $crate::event!(name: $name, $crate::Level::ERROR, {}, $($arg)+) +); + +// Target. +(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*) +); +(target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, { $($k).+ $($field)* }) +); +(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* }) +); +(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* }) +); +(target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, {}, $($arg)+) +); + +// Parent. +(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::ERROR, + { $($field)+ }, + $($arg)+ + ) +); +(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::ERROR, + { $($k).+ = $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::ERROR, + { ?$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::ERROR, + { %$($k).+ = $($field)*} + ) +); +(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::ERROR, + { $($k).+, $($field)*} + ) +); +(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::ERROR, + { ?$($k).+, $($field)*} + ) +); +(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::ERROR, + { %$($k).+, $($field)*} + ) +); +(parent: $parent:expr, $($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + parent: $parent, + $crate::Level::ERROR, + {}, + $($arg)+ + ) +); + +// ... +({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { $($field)+ }, + $($arg)+ + ) +); +($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { $($k).+ = $($field)*} + ) +); +(?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { ?$($k).+ = $($field)*} + ) +); +(%$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { %$($k).+ = $($field)*} + ) +); +($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { $($k).+, $($field)*} + ) +); +(?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { ?$($k).+, $($field)*} + ) +); +(%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { %$($k).+, $($field)*} + ) +); +(?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { ?$($k).+ } + ) +); +(%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { %$($k).+ } + ) +); +($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { $($k).+ } + ) +); +($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + $($arg)+ + ) +); +} + +#[doc(hidden)] +#[macro_export] +macro_rules! callsite { +(name: $name:expr, kind: $kind:expr, fields: $($fields:tt)*) => {{ + $crate::callsite! { + name: $name, + kind: $kind, + target: module_path!(), + level: $crate::Level::TRACE, + fields: $($fields)* + } +}}; +( + name: $name:expr, + kind: $kind:expr, + level: $lvl:expr, + fields: $($fields:tt)* +) => {{ + $crate::callsite! { + name: $name, + kind: $kind, + target: module_path!(), + level: $lvl, + fields: $($fields)* + } +}}; +( + name: $name:expr, + kind: $kind:expr, + target: $target:expr, + level: $lvl:expr, + fields: $($fields:tt)* +) => {{ + static META: $crate::Metadata<'static> = { + $crate::metadata! { + name: $name, + target: $target, + level: $lvl, + fields: $crate::fieldset!( $($fields)* ), + callsite: &__CALLSITE, + kind: $kind, + } + }; + static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite::DefaultCallsite::new(&META); + __CALLSITE.register(); + &__CALLSITE +}}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! callsite2 { +(name: $name:expr, kind: $kind:expr, fields: $($fields:tt)*) => {{ + $crate::callsite2! { + name: $name, + kind: $kind, + target: module_path!(), + level: $crate::Level::TRACE, + fields: $($fields)* + } +}}; +( + name: $name:expr, + kind: $kind:expr, + level: $lvl:expr, + fields: $($fields:tt)* +) => {{ + $crate::callsite2! { + name: $name, + kind: $kind, + target: module_path!(), + level: $lvl, + fields: $($fields)* + } +}}; +( + name: $name:expr, + kind: $kind:expr, + target: $target:expr, + level: $lvl:expr, + fields: $($fields:tt)* +) => {{ + static META: $crate::Metadata<'static> = { + $crate::metadata! { + name: $name, + target: $target, + level: $lvl, + fields: $crate::fieldset!( $($fields)* ), + callsite: &__CALLSITE, + kind: $kind, + } + }; + $crate::callsite::DefaultCallsite::new(&META) +}}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! level_enabled { +($lvl:expr) => { + $lvl <= $crate::level_filters::STATIC_MAX_LEVEL + && $lvl <= $crate::level_filters::LevelFilter::current() +}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! valueset { + +// === base case === +(@ { $(,)* $($val:expr),* $(,)* }, $next:expr $(,)*) => { + &[ $($val),* ] +}; + +(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) }, + $next, + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) }, + $next, + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) }, + $next, + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) }, + $next, + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) }, + $next, + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) }, + $next, + ) +}; + +// Handle literal names +(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) }, + $next, + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) }, + $next, + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) }, + $next, + ) +}; + +// Handle constant names +(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = ?$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = %$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = $val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, + $next, + $($rest)* + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = ?$val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, + $next, + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = %$val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, + $next, + ) +}; +(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = $val:expr) => { + $crate::valueset!( + @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, + $next, + ) +}; + +(@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => { + $crate::valueset!(@ { (&$next, $crate::__macro_support::Option::Some(&$crate::__macro_support::format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, ) +}; + +($fields:expr, $($kvs:tt)+) => { + { + #[allow(unused_imports)] + use $crate::field::{debug, display, Value}; + let mut iter = $fields.iter(); + $fields.value_set($crate::valueset!( + @ { }, + $crate::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"), + $($kvs)+ + )) + } +}; +($fields:expr,) => { + { + $fields.value_set(&[]) + } +}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! fieldset { +(@ { $(,)* $($out:expr),* $(,)* } $(,)*) => { + &[ $($out),* ] +}; + +(@ { $(,)* $($out:expr),* } $($k:ident).+ = ?$val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*) +}; +(@ { $(,)* $($out:expr),* } $($k:ident).+ = %$val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*) +}; +(@ { $(,)* $($out:expr),* } $($k:ident).+ = $val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*) +}; +(@ { $(,)* $($out:expr),* } ?$($k:ident).+, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*) +}; +(@ { $(,)* $($out:expr),* } %$($k:ident).+, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*) +}; +(@ { $(,)* $($out:expr),* } $($k:ident).+, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*) +}; + +// Handle literal names +(@ { $(,)* $($out:expr),* } $k:literal = ?$val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $k } $($rest)*) +}; +(@ { $(,)* $($out:expr),* } $k:literal = %$val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $k } $($rest)*) +}; +(@ { $(,)* $($out:expr),* } $k:literal = $val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $k } $($rest)*) +}; + +// Handle constant names +(@ { $(,)* $($out:expr),* } { $k:expr } = ?$val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $k } $($rest)*) +}; +(@ { $(,)* $($out:expr),* } { $k:expr } = %$val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $k } $($rest)*) +}; +(@ { $(,)* $($out:expr),* } { $k:expr } = $val:expr, $($rest:tt)*) => { + $crate::fieldset!(@ { $($out),*, $k } $($rest)*) +}; + +(@ { $(,)* $($out:expr),* } $($rest:tt)+) => { + $crate::fieldset!(@ { "message", $($out),*, }) +}; + +($($args:tt)*) => { + $crate::fieldset!(@ { } $($args)*,) +}; + +} + +#[cfg(feature = "log")] +#[doc(hidden)] +#[macro_export] +macro_rules! level_to_log { +($level:expr) => { + match $level { + $crate::Level::ERROR => $crate::log::Level::Error, + $crate::Level::WARN => $crate::log::Level::Warn, + $crate::Level::INFO => $crate::log::Level::Info, + $crate::Level::DEBUG => $crate::log::Level::Debug, + _ => $crate::log::Level::Trace, + } +}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __tracing_stringify { +($($t:tt)*) => { + stringify!($($t)*) +}; +} + +#[cfg(not(feature = "log"))] +#[doc(hidden)] +#[macro_export] +macro_rules! __tracing_log { +($level:expr, $callsite:expr, $value_set:expr) => {}; +} + +#[cfg(feature = "log")] +#[doc(hidden)] +#[macro_export] +macro_rules! __tracing_log { +($level:expr, $callsite:expr, $value_set:expr) => { + $crate::if_log_enabled! { $level, { + use $crate::log; + let level = $crate::level_to_log!($level); + if level <= log::max_level() { + let meta = $callsite.metadata(); + let log_meta = log::Metadata::builder() + .level(level) + .target(meta.target()) + .build(); + let logger = log::logger(); + if logger.enabled(&log_meta) { + $crate::__macro_support::__tracing_log(meta, logger, log_meta, $value_set) + } + } + }} +}; +} + +#[cfg(not(feature = "log"))] +#[doc(hidden)] +#[macro_export] +macro_rules! if_log_enabled { +($lvl:expr, $e:expr;) => { + $crate::if_log_enabled! { $lvl, $e } +}; +($lvl:expr, $if_log:block) => { + $crate::if_log_enabled! { $lvl, $if_log else {} } +}; +($lvl:expr, $if_log:block else $else_block:block) => { + $else_block +}; +} + +#[cfg(all(feature = "log", not(feature = "log-always")))] +#[doc(hidden)] +#[macro_export] +macro_rules! if_log_enabled { +($lvl:expr, $e:expr;) => { + $crate::if_log_enabled! { $lvl, $e } +}; +($lvl:expr, $if_log:block) => { + $crate::if_log_enabled! { $lvl, $if_log else {} } +}; +($lvl:expr, $if_log:block else $else_block:block) => { + if $crate::level_to_log!($lvl) <= $crate::log::STATIC_MAX_LEVEL { + if !$crate::dispatcher::has_been_set() { + $if_log + } else { + $else_block + } + } else { + $else_block + } +}; +} + +#[cfg(all(feature = "log", feature = "log-always"))] +#[doc(hidden)] +#[macro_export] +macro_rules! if_log_enabled { +($lvl:expr, $e:expr;) => { + $crate::if_log_enabled! { $lvl, $e } +}; +($lvl:expr, $if_log:block) => { + $crate::if_log_enabled! { $lvl, $if_log else {} } +}; +($lvl:expr, $if_log:block else $else_block:block) => { + if $crate::level_to_log!($lvl) <= $crate::log::STATIC_MAX_LEVEL { + #[allow(unused_braces)] + $if_log + } else { + $else_block + } +}; +} + +//- /lib.rs crate:ra_test_fixture deps:tracing +fn foo() { +tracing::error!(); +} + "#, + &["E0432", "inactive-code", "unresolved-macro-call", "syntax-error", "macro-error"], + ); +} diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index 9255c5a689..acab989437 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs @@ -38,7 +38,10 @@ impl<'t> Bindings<'t> { nesting_state.hit = true; b = match b { Binding::Fragment(_) => break, - Binding::Missing(_) => break, + Binding::Missing(_) => { + nesting_state.at_end = true; + break; + } Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| { nesting_state.at_end = true; binding_err!("could not find nested binding `{name}`") From 8eb5d3c6f3353628c7385ada511d1284b1e63526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 8 Jan 2025 10:19:46 +0200 Subject: [PATCH 15/98] Fix test-fixture autopublishing --- Cargo.toml | 4 ++-- crates/test-fixture/Cargo.toml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9440123de7..629bd74467 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,6 +79,7 @@ span = { path = "./crates/span", version = "0.0.0" } stdx = { path = "./crates/stdx", version = "0.0.0" } syntax = { path = "./crates/syntax", version = "0.0.0" } syntax-bridge = { path = "./crates/syntax-bridge", version = "0.0.0" } +test-fixture = { path = "./crates/test-fixture", version = "0.0.0" } test-utils = { path = "./crates/test-utils", version = "0.0.0" } toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } @@ -93,9 +94,8 @@ ra-ap-rustc_abi = { version = "0.87", default-features = false } ra-ap-rustc_pattern_analysis = { version = "0.87", default-features = false } # local crates that aren't published to crates.io. These should not have versions. -test-fixture = { path = "./crates/test-fixture" } -# In-tree crates that are published separately and follow semver. See lib/README.md +# in-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.2" } la-arena = { version = "0.3.1" } lsp-server = { version = "0.7.6" } diff --git a/crates/test-fixture/Cargo.toml b/crates/test-fixture/Cargo.toml index c860e7b118..95f4cb9d67 100644 --- a/crates/test-fixture/Cargo.toml +++ b/crates/test-fixture/Cargo.toml @@ -2,6 +2,8 @@ name = "test-fixture" version = "0.0.0" rust-version.workspace = true +description = "Test fixtures for rust-analyzer." + edition.workspace = true license.workspace = true authors.workspace = true From acccd4bde61dd6cd0677e6f6881e312babcc9dea Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 8 Jan 2025 10:59:58 +0100 Subject: [PATCH 16/98] fix: Fix `env`/`option_env` macro check disregarding macro_rules definitions --- crates/hir/src/lib.rs | 17 ++++-- .../src/completions/env_vars.rs | 59 +++++++++---------- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 00b4db5437..b7c3e6bb41 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -3046,14 +3046,23 @@ impl Macro { MacroId::Macro2Id(it) => { matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env()) } - MacroId::MacroRulesId(_) | MacroId::ProcMacroId(_) => false, + MacroId::MacroRulesId(it) => { + matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env()) + } + MacroId::ProcMacroId(_) => false, } } pub fn is_asm_or_global_asm(&self, db: &dyn HirDatabase) -> bool { - matches!(self.id, MacroId::Macro2Id(it) if { - matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm()) - }) + match self.id { + MacroId::Macro2Id(it) => { + matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm()) + } + MacroId::MacroRulesId(it) => { + matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm()) + } + MacroId::ProcMacroId(_) => false, + } } pub fn is_attr(&self, db: &dyn HirDatabase) -> bool { diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 0b6790d42a..40af5203e9 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -68,43 +68,40 @@ pub(crate) fn complete_cargo_env_vars( mod tests { use crate::tests::{check_edit, completion_list}; - fn check(macro_name: &str) { - check_edit( - "CARGO_BIN_NAME", - &format!( - r#" - #[rustc_builtin_macro] - macro {macro_name} {{ - ($var:literal) => {{ 0 }} - }} - - fn main() {{ - let foo = {macro_name}!("CAR$0"); - }} - "# - ), - &format!( - r#" - #[rustc_builtin_macro] - macro {macro_name} {{ - ($var:literal) => {{ 0 }} - }} - - fn main() {{ - let foo = {macro_name}!("CARGO_BIN_NAME"); - }} - "# - ), - ); - } #[test] fn completes_env_variable_in_env() { - check("env") + check_edit( + "CARGO_BIN_NAME", + r#" +//- minicore: env +fn main() { + let foo = env!("CAR$0"); +} + "#, + r#" +fn main() { + let foo = env!("CARGO_BIN_NAME"); +} + "#, + ); } #[test] fn completes_env_variable_in_option_env() { - check("option_env"); + check_edit( + "CARGO_BIN_NAME", + r#" +//- minicore: env +fn main() { + let foo = option_env!("CAR$0"); +} + "#, + r#" +fn main() { + let foo = option_env!("CARGO_BIN_NAME"); +} + "#, + ); } #[test] From 1c5a125beb35725ccc2ade005db9870db734bf23 Mon Sep 17 00:00:00 2001 From: qjerome Date: Wed, 8 Jan 2025 11:20:08 +0100 Subject: [PATCH 17/98] refactor: struct holding cargo cfgs settings --- crates/rust-analyzer/src/config.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 30f0031905..1fe8d0ce42 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -571,12 +571,10 @@ config_data! { /// avoid checking unnecessary things. cargo_buildScripts_useRustcWrapper: bool = true, /// List of cfg options to enable with the given values. - cargo_cfgs: FxHashMap> = { - let mut m = FxHashMap::default(); - m.insert("debug_assertions".to_owned(), None); - m.insert("miri".to_owned(), None); - m - }, + cargo_cfgs: Vec = { + vec!["debug_assertion".into(), "miri".into()] + } + , /// Extra arguments that are passed to every cargo invocation. cargo_extraArgs: Vec = vec![], /// Extra environment variables that will be set when running cargo, rustc @@ -1944,6 +1942,17 @@ impl Config { global: CfgDiff::new( self.cargo_cfgs(source_root) .iter() + // parse any cfg setting formatted as key=value + .map(|s| { + let mut sp = s.splitn(2, "="); + let key = sp.next(); + let val = sp.next(); + (key, val) + }) + // we filter out anything with a None key + .filter(|(key, _)| key.is_some()) + // unwrap cannot panic here as we are sure key is Some + .map(|(key, val)| (key.unwrap(), val)) .map(|(key, val)| match val { Some(val) => CfgAtom::KeyValue { key: Symbol::intern(key), From e9a13ab6d795c0672530db048f308c6beba747c5 Mon Sep 17 00:00:00 2001 From: qjerome Date: Wed, 8 Jan 2025 14:47:21 +0100 Subject: [PATCH 18/98] fix: autogenerate files --- docs/user/generated_config.adoc | 8 ++++---- editors/code/package.json | 13 ++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 5b86766aa8..c6f5852f87 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -94,10 +94,10 @@ avoid checking unnecessary things. -- Default: ---- -{ - "miri": null, - "debug_assertions": null -} +[ + "debug_assertion", + "miri" +] ---- List of cfg options to enable with the given values. diff --git a/editors/code/package.json b/editors/code/package.json index 80246bf3fe..6cb74a94a0 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -791,11 +791,14 @@ "properties": { "rust-analyzer.cargo.cfgs": { "markdownDescription": "List of cfg options to enable with the given values.", - "default": { - "miri": null, - "debug_assertions": null - }, - "type": "object" + "default": [ + "debug_assertion", + "miri" + ], + "type": "array", + "items": { + "type": "string" + } } } }, From cc7fb1945d997430d29d7a95044e7755f25e53d6 Mon Sep 17 00:00:00 2001 From: qjerome Date: Wed, 8 Jan 2025 14:47:46 +0100 Subject: [PATCH 19/98] =?UTF-8?q?fix:=C2=A0requested=20changed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/rust-analyzer/src/config.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 1fe8d0ce42..051871020a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -573,8 +573,7 @@ config_data! { /// List of cfg options to enable with the given values. cargo_cfgs: Vec = { vec!["debug_assertion".into(), "miri".into()] - } - , + }, /// Extra arguments that are passed to every cargo invocation. cargo_extraArgs: Vec = vec![], /// Extra environment variables that will be set when running cargo, rustc @@ -1942,17 +1941,13 @@ impl Config { global: CfgDiff::new( self.cargo_cfgs(source_root) .iter() - // parse any cfg setting formatted as key=value - .map(|s| { + // parse any cfg setting formatted as key=value or just key (without value) + .filter_map(|s| { let mut sp = s.splitn(2, "="); let key = sp.next(); let val = sp.next(); - (key, val) + key.map(|key| (key, val)) }) - // we filter out anything with a None key - .filter(|(key, _)| key.is_some()) - // unwrap cannot panic here as we are sure key is Some - .map(|(key, val)| (key.unwrap(), val)) .map(|(key, val)| match val { Some(val) => CfgAtom::KeyValue { key: Symbol::intern(key), From b21a5f83d2a970d6d2ace80b381b9a53f53d3662 Mon Sep 17 00:00:00 2001 From: Vishruth-Thimmaiah Date: Wed, 8 Jan 2025 22:53:09 +0530 Subject: [PATCH 20/98] refactor test helpers within ide-completions --- crates/ide-completion/src/completions/dot.rs | 108 ++++++++---------- .../src/completions/extern_abi.rs | 15 +-- .../src/completions/format_string.rs | 13 +-- .../src/completions/item_list/trait_impl.rs | 41 +++---- .../ide-completion/src/completions/keyword.rs | 9 +- .../src/completions/lifetime.rs | 9 +- crates/ide-completion/src/completions/mod_.rs | 9 +- .../ide-completion/src/completions/postfix.rs | 9 +- crates/ide-completion/src/tests.rs | 25 +++- crates/ide-completion/src/tests/attribute.rs | 51 +++------ crates/ide-completion/src/tests/expression.rs | 87 +++++++------- crates/ide-completion/src/tests/fn_param.rs | 16 +-- crates/ide-completion/src/tests/item.rs | 41 +++---- crates/ide-completion/src/tests/item_list.rs | 53 ++++----- crates/ide-completion/src/tests/pattern.rs | 93 +++++++-------- crates/ide-completion/src/tests/predicate.rs | 25 ++-- .../ide-completion/src/tests/proc_macros.rs | 9 +- crates/ide-completion/src/tests/record.rs | 9 +- crates/ide-completion/src/tests/special.rs | 24 +--- crates/ide-completion/src/tests/type_pos.rs | 99 ++++++++-------- crates/ide-completion/src/tests/use_tree.rs | 9 +- crates/ide-completion/src/tests/visibility.rs | 16 +-- 22 files changed, 322 insertions(+), 448 deletions(-) diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 26074672ba..8d44dbab60 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -214,25 +214,13 @@ fn complete_methods( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::expect; - use crate::tests::{ - check_edit, completion_list_no_kw, completion_list_no_kw_with_private_editable, - }; - - fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list_no_kw(ra_fixture); - expect.assert_eq(&actual); - } - - fn check_with_private_editable(ra_fixture: &str, expect: Expect) { - let actual = completion_list_no_kw_with_private_editable(ra_fixture); - expect.assert_eq(&actual); - } + use crate::tests::{check_edit, check_no_kw, check_with_private_editable}; #[test] fn test_struct_field_and_method_completion() { - check( + check_no_kw( r#" struct S { foo: u32 } impl S { @@ -249,7 +237,7 @@ fn foo(s: S) { s.$0 } #[test] fn no_unstable_method_on_stable() { - check( + check_no_kw( r#" //- /main.rs crate:main deps:std fn foo(s: std::S) { s.$0 } @@ -266,7 +254,7 @@ impl S { #[test] fn unstable_method_on_nightly() { - check( + check_no_kw( r#" //- toolchain:nightly //- /main.rs crate:main deps:std @@ -286,7 +274,7 @@ impl S { #[test] fn test_struct_field_completion_self() { - check( + check_no_kw( r#" struct S { the_field: (u32,) } impl S { @@ -302,7 +290,7 @@ impl S { #[test] fn test_struct_field_completion_autoderef() { - check( + check_no_kw( r#" struct A { the_field: (u32, i32) } impl A { @@ -318,7 +306,7 @@ impl A { #[test] fn test_no_struct_field_completion_for_method_call() { - check( + check_no_kw( r#" struct A { the_field: u32 } fn foo(a: A) { a.$0() } @@ -329,7 +317,7 @@ fn foo(a: A) { a.$0() } #[test] fn test_visibility_filtering() { - check( + check_no_kw( r#" //- /lib.rs crate:lib new_source_root:local pub mod m { @@ -348,7 +336,7 @@ fn foo(a: lib::m::A) { a.$0 } "#]], ); - check( + check_no_kw( r#" //- /lib.rs crate:lib new_source_root:library pub mod m { @@ -367,7 +355,7 @@ fn foo(a: lib::m::A) { a.$0 } "#]], ); - check( + check_no_kw( r#" //- /lib.rs crate:lib new_source_root:library pub mod m { @@ -384,7 +372,7 @@ fn foo(a: lib::m::A) { a.$0 } "#]], ); - check( + check_no_kw( r#" //- /lib.rs crate:lib new_source_root:local pub struct A {} @@ -402,7 +390,7 @@ fn foo(a: lib::A) { a.$0 } me pub_method() fn(&self) "#]], ); - check( + check_no_kw( r#" //- /lib.rs crate:lib new_source_root:library pub struct A {} @@ -524,7 +512,7 @@ fn foo(a: lib::A) { a.$0 } #[test] fn test_local_impls() { - check( + check_no_kw( r#" pub struct A {} mod m { @@ -553,7 +541,7 @@ fn foo(a: A) { #[test] fn test_doc_hidden_filtering() { - check( + check_no_kw( r#" //- /lib.rs crate:lib deps:dep fn foo(a: dep::A) { a.$0 } @@ -580,7 +568,7 @@ impl A { #[test] fn test_union_field_completion() { - check( + check_no_kw( r#" union U { field: u8, other: u16 } fn foo(u: U) { u.$0 } @@ -594,7 +582,7 @@ fn foo(u: U) { u.$0 } #[test] fn test_method_completion_only_fitting_impls() { - check( + check_no_kw( r#" struct A {} impl A { @@ -613,7 +601,7 @@ fn foo(a: A) { a.$0 } #[test] fn test_trait_method_completion() { - check( + check_no_kw( r#" struct A {} trait Trait { fn the_method(&self); } @@ -643,7 +631,7 @@ fn foo(a: A) { a.the_method();$0 } #[test] fn test_trait_method_completion_deduplicated() { - check( + check_no_kw( r" struct A {} trait Trait { fn the_method(&self); } @@ -658,7 +646,7 @@ fn foo(a: &A) { a.$0 } #[test] fn completes_trait_method_from_other_module() { - check( + check_no_kw( r" struct A {} mod m { @@ -676,7 +664,7 @@ fn foo(a: A) { a.$0 } #[test] fn test_no_non_self_method() { - check( + check_no_kw( r#" struct A {} impl A { @@ -692,7 +680,7 @@ fn foo(a: A) { #[test] fn test_tuple_field_completion() { - check( + check_no_kw( r#" fn foo() { let b = (0, 3.14); @@ -708,7 +696,7 @@ fn foo() { #[test] fn test_tuple_struct_field_completion() { - check( + check_no_kw( r#" struct S(i32, f64); fn foo() { @@ -725,7 +713,7 @@ fn foo() { #[test] fn test_tuple_field_inference() { - check( + check_no_kw( r#" pub struct S; impl S { pub fn blah(&self) {} } @@ -747,7 +735,7 @@ impl T { #[test] fn test_field_no_same_name() { - check( + check_no_kw( r#" //- minicore: deref struct A { field: u8 } @@ -770,7 +758,7 @@ fn test(a: A) { #[test] fn test_tuple_field_no_same_index() { - check( + check_no_kw( r#" //- minicore: deref struct A(u8); @@ -793,7 +781,7 @@ fn test(a: A) { #[test] fn test_tuple_struct_deref_to_tuple_no_same_index() { - check( + check_no_kw( r#" //- minicore: deref struct A(u8); @@ -815,7 +803,7 @@ fn test(a: A) { #[test] fn test_completion_works_in_consts() { - check( + check_no_kw( r#" struct A { the_field: u32 } const X: u32 = { @@ -830,7 +818,7 @@ const X: u32 = { #[test] fn works_in_simple_macro_1() { - check( + check_no_kw( r#" macro_rules! m { ($e:expr) => { $e } } struct A { the_field: u32 } @@ -847,7 +835,7 @@ fn foo(a: A) { #[test] fn works_in_simple_macro_2() { // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery - check( + check_no_kw( r#" macro_rules! m { ($e:expr) => { $e } } struct A { the_field: u32 } @@ -863,7 +851,7 @@ fn foo(a: A) { #[test] fn works_in_simple_macro_recursive_1() { - check( + check_no_kw( r#" macro_rules! m { ($e:expr) => { $e } } struct A { the_field: u32 } @@ -879,7 +867,7 @@ fn foo(a: A) { #[test] fn macro_expansion_resilient() { - check( + check_no_kw( r#" macro_rules! d { () => {}; @@ -905,7 +893,7 @@ fn foo(a: A) { #[test] fn test_method_completion_issue_3547() { - check( + check_no_kw( r#" struct HashSet {} impl HashSet { @@ -924,7 +912,7 @@ fn foo() { #[test] fn completes_method_call_when_receiver_is_a_macro_call() { - check( + check_no_kw( r#" struct S; impl S { fn foo(&self) {} } @@ -939,7 +927,7 @@ fn main() { make_s!().f$0; } #[test] fn completes_after_macro_call_in_submodule() { - check( + check_no_kw( r#" macro_rules! empty { () => {}; @@ -967,7 +955,7 @@ mod foo { #[test] fn issue_8931() { - check( + check_no_kw( r#" //- minicore: fn struct S; @@ -994,7 +982,7 @@ impl S { #[test] fn completes_bare_fields_and_methods_in_methods() { - check( + check_no_kw( r#" struct Foo { field: i32 } @@ -1008,7 +996,7 @@ impl Foo { fn foo(&self) { $0 } }"#, bt u32 u32 "#]], ); - check( + check_no_kw( r#" struct Foo(i32); @@ -1026,7 +1014,7 @@ impl Foo { fn foo(&mut self) { $0 } }"#, #[test] fn macro_completion_after_dot() { - check( + check_no_kw( r#" macro_rules! m { ($e:expr) => { $e }; @@ -1051,7 +1039,7 @@ fn f() { #[test] fn completes_method_call_when_receiver_type_has_errors_issue_10297() { - check( + check_no_kw( r#" //- minicore: iterator, sized struct Vec; @@ -1102,7 +1090,7 @@ fn main() { #[test] fn issue_12484() { - check( + check_no_kw( r#" //- minicore: sized trait SizeUser { @@ -1124,7 +1112,7 @@ fn test(thing: impl Encrypt) { #[test] fn only_consider_same_type_once() { - check( + check_no_kw( r#" //- minicore: deref struct A(u8); @@ -1150,7 +1138,7 @@ fn test(a: A) { #[test] fn no_inference_var_in_completion() { - check( + check_no_kw( r#" struct S(T); fn test(s: S) { @@ -1165,7 +1153,7 @@ fn test(s: S) { #[test] fn assoc_impl_1() { - check( + check_no_kw( r#" //- minicore: deref fn main() { @@ -1206,7 +1194,7 @@ impl> Foo { #[test] fn assoc_impl_2() { - check( + check_no_kw( r#" //- minicore: deref fn main() { @@ -1242,7 +1230,7 @@ impl> Foo { #[test] fn test_struct_function_field_completion() { - check( + check_no_kw( r#" struct S { va_field: u32, fn_field: fn() } fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() } @@ -1267,7 +1255,7 @@ fn foo() { (S { va_field: 0, fn_field: || {} }.fn_field)() } #[test] fn test_tuple_function_field_completion() { - check( + check_no_kw( r#" struct B(u32, fn()) fn foo() { @@ -1301,7 +1289,7 @@ fn foo() { #[test] fn test_fn_field_dot_access_method_has_parens_false() { - check( + check_no_kw( r#" struct Foo { baz: fn() } impl Foo { diff --git a/crates/ide-completion/src/completions/extern_abi.rs b/crates/ide-completion/src/completions/extern_abi.rs index bcfda928c4..7c2cc2a6c1 100644 --- a/crates/ide-completion/src/completions/extern_abi.rs +++ b/crates/ide-completion/src/completions/extern_abi.rs @@ -65,18 +65,13 @@ pub(crate) fn complete_extern_abi( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::expect; - use crate::tests::{check_edit, completion_list_no_kw}; - - fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list_no_kw(ra_fixture); - expect.assert_eq(&actual); - } + use crate::tests::{check_edit, check_no_kw}; #[test] fn only_completes_in_string_literals() { - check( + check_no_kw( r#" $0 fn foo {} "#, @@ -86,7 +81,7 @@ $0 fn foo {} #[test] fn requires_extern_prefix() { - check( + check_no_kw( r#" "$0" fn foo {} "#, @@ -96,7 +91,7 @@ $0 fn foo {} #[test] fn works() { - check( + check_no_kw( r#" extern "$0" fn foo {} "#, diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs index a87c60c694..dcd40c3412 100644 --- a/crates/ide-completion/src/completions/format_string.rs +++ b/crates/ide-completion/src/completions/format_string.rs @@ -61,18 +61,13 @@ pub(crate) fn format_string( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::expect; - use crate::tests::{check_edit, completion_list_no_kw}; - - fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list_no_kw(ra_fixture); - expect.assert_eq(&actual); - } + use crate::tests::{check_edit, check_no_kw}; #[test] fn works_when_wrapped() { - check( + check_no_kw( r#" //- minicore: fmt macro_rules! print { @@ -89,7 +84,7 @@ fn main() { #[test] fn no_completion_without_brace() { - check( + check_no_kw( r#" //- minicore: fmt fn main() { diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index 80d72b460f..6d1945c453 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -514,18 +514,13 @@ fn function_declaration( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::expect; - use crate::tests::{check_edit, completion_list_no_kw}; - - fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list_no_kw(ra_fixture); - expect.assert_eq(&actual) - } + use crate::tests::{check_edit, check_no_kw}; #[test] fn no_completion_inside_fn() { - check( + check_no_kw( r" trait Test { fn test(); fn test2(); } struct T; @@ -544,7 +539,7 @@ impl Test for T { "#]], ); - check( + check_no_kw( r" trait Test { fn test(); fn test2(); } struct T; @@ -558,7 +553,7 @@ impl Test for T { expect![[""]], ); - check( + check_no_kw( r" trait Test { fn test(); fn test2(); } struct T; @@ -573,7 +568,7 @@ impl Test for T { ); // https://github.com/rust-lang/rust-analyzer/pull/5976#issuecomment-692332191 - check( + check_no_kw( r" trait Test { fn test(); fn test2(); } struct T; @@ -587,7 +582,7 @@ impl Test for T { expect![[r#""#]], ); - check( + check_no_kw( r" trait Test { fn test(_: i32); fn test2(); } struct T; @@ -606,7 +601,7 @@ impl Test for T { "#]], ); - check( + check_no_kw( r" trait Test { fn test(_: fn()); fn test2(); } struct T; @@ -624,7 +619,7 @@ impl Test for T { #[test] fn no_completion_inside_const() { - check( + check_no_kw( r" trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); } struct T; @@ -636,7 +631,7 @@ impl Test for T { expect![[r#""#]], ); - check( + check_no_kw( r" trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } struct T; @@ -653,7 +648,7 @@ impl Test for T { "#]], ); - check( + check_no_kw( r" trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } struct T; @@ -670,7 +665,7 @@ impl Test for T { "#]], ); - check( + check_no_kw( r" trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } struct T; @@ -689,7 +684,7 @@ impl Test for T { "#]], ); - check( + check_no_kw( r" trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } struct T; @@ -703,7 +698,7 @@ impl Test for T { expect![[""]], ); - check( + check_no_kw( r" trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } struct T; @@ -720,7 +715,7 @@ impl Test for T { #[test] fn no_completion_inside_type() { - check( + check_no_kw( r" trait Test { type Test; type Test2; fn test(); } struct T; @@ -737,7 +732,7 @@ impl Test for T { "#]], ); - check( + check_no_kw( r" trait Test { type Test; type Test2; fn test(); } struct T; @@ -1263,7 +1258,7 @@ impl Foo for Bar { #[test] fn works_directly_in_impl() { - check( + check_no_kw( r#" trait Tr { fn required(); @@ -1277,7 +1272,7 @@ impl Tr for () { fn fn required() "#]], ); - check( + check_no_kw( r#" trait Tr { fn provided() {} diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index 4700ed6c1a..6541ee502d 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -32,14 +32,9 @@ pub(crate) fn complete_for_and_where( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::expect; - use crate::tests::{check_edit, completion_list}; - - fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual) - } + use crate::tests::{check, check_edit}; #[test] fn test_else_edit_after_if() { diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs index 0692446381..53a62fe49c 100644 --- a/crates/ide-completion/src/completions/lifetime.rs +++ b/crates/ide-completion/src/completions/lifetime.rs @@ -59,14 +59,9 @@ pub(crate) fn complete_label( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::expect; - use crate::tests::{check_edit, completion_list}; - - fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual); - } + use crate::tests::{check, check_edit}; #[test] fn check_lifetime_edit() { diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs index f12f011a6b..bafe329420 100644 --- a/crates/ide-completion/src/completions/mod_.rs +++ b/crates/ide-completion/src/completions/mod_.rs @@ -159,14 +159,9 @@ fn module_chain_to_containing_module_file( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::expect; - use crate::tests::completion_list; - - fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual); - } + use crate::tests::check; #[test] fn lib_module_completion() { diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 5bdc320d57..67ea05e002 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -401,18 +401,13 @@ fn add_custom_postfix_completions( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::expect; use crate::{ - tests::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG}, + tests::{check, check_edit, check_edit_with_config, TEST_CONFIG}, CompletionConfig, Snippet, }; - fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual) - } - #[test] fn postfix_completion_works_for_trivial_path_expression() { check( diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index 1815f34053..71d143a2b7 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -253,11 +253,34 @@ pub(crate) fn check_edit_with_config( assert_eq_text!(&ra_fixture_after, &actual) } -fn check_empty(ra_fixture: &str, expect: Expect) { +pub(crate) fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(ra_fixture); expect.assert_eq(&actual); } +pub(crate) fn check_with_base_items(ra_fixture: &str, expect: Expect) { + check(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"), expect) +} + +pub(crate) fn check_no_kw(ra_fixture: &str, expect: Expect) { + let actual = completion_list_no_kw(ra_fixture); + expect.assert_eq(&actual) +} + +pub(crate) fn check_with_private_editable(ra_fixture: &str, expect: Expect) { + let actual = completion_list_no_kw_with_private_editable(ra_fixture); + expect.assert_eq(&actual); +} + +pub(crate) fn check_with_trigger_character( + ra_fixture: &str, + trigger_character: Option, + expect: Expect, +) { + let actual = completion_list_with_trigger_character(ra_fixture, trigger_character); + expect.assert_eq(&actual) +} + pub(crate) fn get_all_items( config: CompletionConfig<'_>, code: &str, diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs index ebf3582057..32d3b50f23 100644 --- a/crates/ide-completion/src/tests/attribute.rs +++ b/crates/ide-completion/src/tests/attribute.rs @@ -1,12 +1,7 @@ //! Completion tests for attributes. -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::{check_edit, completion_list}; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual); -} +use crate::tests::{check, check_edit}; #[test] fn derive_helpers() { @@ -788,14 +783,9 @@ mod cfg { mod derive { use super::*; - fn check_derive(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual); - } - #[test] fn no_completion_for_incorrect_derive() { - check_derive( + check( r#" //- minicore: derive, copy, clone, ord, eq, default, fmt #[derive{$0)] struct Test; @@ -806,7 +796,7 @@ mod derive { #[test] fn empty_derive() { - check_derive( + check( r#" //- minicore: derive, copy, clone, ord, eq, default, fmt #[derive($0)] struct Test; @@ -828,7 +818,7 @@ mod derive { #[test] fn derive_with_input_before() { - check_derive( + check( r#" //- minicore: derive, copy, clone, ord, eq, default, fmt #[derive(serde::Serialize, PartialEq, $0)] struct Test; @@ -849,7 +839,7 @@ mod derive { #[test] fn derive_with_input_after() { - check_derive( + check( r#" //- minicore: derive, copy, clone, ord, eq, default, fmt #[derive($0 serde::Serialize, PartialEq)] struct Test; @@ -870,7 +860,7 @@ mod derive { #[test] fn derive_with_existing_derives() { - check_derive( + check( r#" //- minicore: derive, copy, clone, ord, eq, default, fmt #[derive(PartialEq, Eq, Or$0)] struct Test; @@ -890,7 +880,7 @@ mod derive { #[test] fn derive_flyimport() { - check_derive( + check( r#" //- proc_macros: derive_identity //- minicore: derive @@ -904,7 +894,7 @@ mod derive { kw self:: "#]], ); - check_derive( + check( r#" //- proc_macros: derive_identity //- minicore: derive @@ -940,7 +930,7 @@ use proc_macros::DeriveIdentity; #[test] fn qualified() { - check_derive( + check( r#" //- proc_macros: derive_identity //- minicore: derive, copy, clone @@ -950,7 +940,7 @@ use proc_macros::DeriveIdentity; de DeriveIdentity proc_macro DeriveIdentity "#]], ); - check_derive( + check( r#" //- proc_macros: derive_identity //- minicore: derive, copy, clone @@ -1056,19 +1046,14 @@ mod lint { mod repr { use super::*; - fn check_repr(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual); - } - #[test] fn no_completion_for_incorrect_repr() { - check_repr(r#"#[repr{$0)] struct Test;"#, expect![[]]) + check(r#"#[repr{$0)] struct Test;"#, expect![[]]) } #[test] fn empty() { - check_repr( + check( r#"#[repr($0)] struct Test;"#, expect![[r#" ba C @@ -1093,12 +1078,12 @@ mod repr { #[test] fn transparent() { - check_repr(r#"#[repr(transparent, $0)] struct Test;"#, expect![[r#""#]]); + check(r#"#[repr(transparent, $0)] struct Test;"#, expect![[r#""#]]); } #[test] fn align() { - check_repr( + check( r#"#[repr(align(1), $0)] struct Test;"#, expect![[r#" ba C @@ -1121,7 +1106,7 @@ mod repr { #[test] fn packed() { - check_repr( + check( r#"#[repr(packed, $0)] struct Test;"#, expect![[r#" ba C @@ -1144,7 +1129,7 @@ mod repr { #[test] fn c() { - check_repr( + check( r#"#[repr(C, $0)] struct Test;"#, expect![[r#" ba align($0) @@ -1167,7 +1152,7 @@ mod repr { #[test] fn prim() { - check_repr( + check( r#"#[repr(usize, $0)] struct Test;"#, expect![[r#" ba C diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 3046614868..3cc7fc9d92 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -4,17 +4,12 @@ use expect_test::{expect, Expect}; use crate::{ config::AutoImportExclusionType, tests::{ - check_edit, check_empty, completion_list, completion_list_with_config, BASE_ITEMS_FIXTURE, + check, check_edit, check_with_base_items, completion_list_with_config, BASE_ITEMS_FIXTURE, TEST_CONFIG, }, CompletionConfig, }; -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); - expect.assert_eq(&actual) -} - fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) { let actual = completion_list_with_config( config, @@ -28,7 +23,7 @@ fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Exp #[test] fn complete_literal_struct_with_a_private_field() { // `FooDesc.bar` is private, the completion should not be triggered. - check( + check_with_base_items( r#" mod _69latrick { pub struct FooDesc { pub six: bool, pub neuf: Vec, bar: bool } @@ -79,7 +74,7 @@ fn baz() { #[test] fn completes_various_bindings() { - check_empty( + check( r#" fn func(param0 @ (param1, param2): (i32, i32)) { let letlocal = 92; @@ -125,7 +120,7 @@ fn func(param0 @ (param1, param2): (i32, i32)) { #[test] fn completes_all_the_things_in_fn_body() { - check( + check_with_base_items( r#" use non_existent::Unresolved; mod qualified { pub enum Enum { Variant } } @@ -191,7 +186,7 @@ impl Unit { ?? Unresolved "#]], ); - check( + check_with_base_items( r#" use non_existent::Unresolved; mod qualified { pub enum Enum { Variant } } @@ -224,7 +219,7 @@ impl Unit { #[test] fn complete_in_block() { - check_empty( + check( r#" fn foo() { if true { @@ -273,7 +268,7 @@ fn complete_in_block() { #[test] fn complete_after_if_expr() { - check_empty( + check( r#" fn foo() { if true {} @@ -321,7 +316,7 @@ fn complete_after_if_expr() { #[test] fn complete_in_match_arm() { - check_empty( + check( r#" fn foo() { match () { @@ -351,7 +346,7 @@ fn complete_in_match_arm() { #[test] fn completes_in_loop_ctx() { - check_empty( + check( r"fn my() { loop { $0 } }", expect![[r#" fn my() fn() @@ -390,7 +385,7 @@ fn completes_in_loop_ctx() { sn ppd "#]], ); - check_empty( + check( r"fn my() { loop { foo.$0 } }", expect![[r#" sn box Box::new(expr) @@ -415,7 +410,7 @@ fn completes_in_loop_ctx() { #[test] fn completes_in_let_initializer() { - check_empty( + check( r#"fn main() { let _ = $0 }"#, expect![[r#" fn main() fn() @@ -439,7 +434,7 @@ fn completes_in_let_initializer() { #[test] fn struct_initializer_field_expr() { - check_empty( + check( r#" struct Foo { pub f: i32, @@ -475,7 +470,7 @@ fn foo() { fn shadowing_shows_single_completion() { cov_mark::check!(shadowing_shows_single_completion); - check_empty( + check( r#" fn foo() { let bar = 92; @@ -508,7 +503,7 @@ fn foo() { #[test] fn in_macro_expr_frag() { - check_empty( + check( r#" macro_rules! m { ($e:expr) => { $e } } fn quux(x: i32) { @@ -535,7 +530,7 @@ fn quux(x: i32) { kw while let "#]], ); - check_empty( + check( r" macro_rules! m { ($e:expr) => { $e } } fn quux(x: i32) { @@ -562,7 +557,7 @@ fn quux(x: i32) { kw while let "#]], ); - check_empty( + check( r#" macro_rules! m { ($e:expr) => { $e } } fn quux(x: i32) { @@ -595,7 +590,7 @@ fn quux(x: i32) { #[test] fn enum_qualified() { - check( + check_with_base_items( r#" impl Enum { type AssocType = (); @@ -619,7 +614,7 @@ fn func() { #[test] fn ty_qualified_no_drop() { - check_empty( + check( r#" //- minicore: drop struct Foo; @@ -636,7 +631,7 @@ fn func() { #[test] fn with_parens() { - check_empty( + check( r#" enum Enum { Variant() @@ -657,7 +652,7 @@ fn func() { #[test] fn detail_impl_trait_in_return_position() { - check_empty( + check( r" //- minicore: sized trait Trait {} @@ -676,7 +671,7 @@ fn main() { #[test] fn detail_async_fn() { - check_empty( + check( r#" //- minicore: future, sized trait Trait {} @@ -697,7 +692,7 @@ fn main() { #[test] fn detail_impl_trait_in_argument_position() { - check_empty( + check( r" //- minicore: sized trait Trait {} @@ -717,7 +712,7 @@ fn main() { #[test] fn complete_record_expr_path() { - check( + check_with_base_items( r#" struct Zulu; impl Zulu { @@ -738,7 +733,7 @@ fn main() { #[test] fn variant_with_struct() { - check_empty( + check( r#" pub struct YoloVariant { pub f: usize @@ -813,7 +808,7 @@ fn return_value_no_block() { #[test] fn else_completion_after_if() { - check_empty( + check( r#" fn foo() { if foo {} $0 } "#, @@ -854,7 +849,7 @@ fn foo() { if foo {} $0 } sn ppd "#]], ); - check_empty( + check( r#" fn foo() { if foo {} el$0 } "#, @@ -895,7 +890,7 @@ fn foo() { if foo {} el$0 } sn ppd "#]], ); - check_empty( + check( r#" fn foo() { bar(if foo {} $0) } "#, @@ -919,7 +914,7 @@ fn foo() { bar(if foo {} $0) } kw while let "#]], ); - check_empty( + check( r#" fn foo() { bar(if foo {} el$0) } "#, @@ -943,7 +938,7 @@ fn foo() { bar(if foo {} el$0) } kw while let "#]], ); - check_empty( + check( r#" fn foo() { if foo {} $0 let x = 92; } "#, @@ -984,7 +979,7 @@ fn foo() { if foo {} $0 let x = 92; } sn ppd "#]], ); - check_empty( + check( r#" fn foo() { if foo {} el$0 let x = 92; } "#, @@ -1025,7 +1020,7 @@ fn foo() { if foo {} el$0 let x = 92; } sn ppd "#]], ); - check_empty( + check( r#" fn foo() { if foo {} el$0 { let x = 92; } } "#, @@ -1070,7 +1065,7 @@ fn foo() { if foo {} el$0 { let x = 92; } } #[test] fn expr_no_unstable_item_on_stable() { - check_empty( + check( r#" //- /main.rs crate:main deps:std use std::*; @@ -1121,7 +1116,7 @@ pub struct UnstableThisShouldNotBeListed; #[test] fn expr_unstable_item_on_nightly() { - check_empty( + check( r#" //- toolchain:nightly //- /main.rs crate:main deps:std @@ -1174,7 +1169,7 @@ pub struct UnstableButWeAreOnNightlyAnyway; #[test] fn inside_format_args_completions_work() { - check_empty( + check( r#" //- minicore: fmt struct Foo; @@ -1200,7 +1195,7 @@ fn main() { sn unsafe unsafe {} "#]], ); - check_empty( + check( r#" //- minicore: fmt struct Foo; @@ -1230,7 +1225,7 @@ fn main() { #[test] fn inside_faulty_format_args_completions_work() { - check_empty( + check( r#" //- minicore: fmt struct Foo; @@ -1256,7 +1251,7 @@ fn main() { sn unsafe unsafe {} "#]], ); - check_empty( + check( r#" //- minicore: fmt struct Foo; @@ -1282,7 +1277,7 @@ fn main() { sn unsafe unsafe {} "#]], ); - check_empty( + check( r#" //- minicore: fmt struct Foo; @@ -1308,7 +1303,7 @@ fn main() { sn unsafe unsafe {} "#]], ); - check_empty( + check( r#" //- minicore: fmt struct Foo; @@ -1340,7 +1335,7 @@ fn main() { #[test] fn macro_that_ignores_completion_marker() { - check( + check_with_base_items( r#" macro_rules! helper { ($v:ident) => {}; @@ -1788,7 +1783,7 @@ fn foo() { #[test] fn hide_ragennew_synthetic_identifiers() { - check_empty( + check( r#" //- minicore: iterator fn bar() { diff --git a/crates/ide-completion/src/tests/fn_param.rs b/crates/ide-completion/src/tests/fn_param.rs index 4a89f874e1..451ce07c74 100644 --- a/crates/ide-completion/src/tests/fn_param.rs +++ b/crates/ide-completion/src/tests/fn_param.rs @@ -1,16 +1,6 @@ -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::{completion_list, completion_list_with_trigger_character}; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual); -} - -fn check_with_trigger_character(ra_fixture: &str, trigger_character: char, expect: Expect) { - let actual = completion_list_with_trigger_character(ra_fixture, Some(trigger_character)); - expect.assert_eq(&actual) -} +use crate::tests::{check, check_with_trigger_character}; #[test] fn only_param() { @@ -124,7 +114,7 @@ fn trigger_by_l_paren() { r#" fn foo($0) "#, - '(', + Some('('), expect![[]], ) } diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs index 79561a0419..bea6d60769 100644 --- a/crates/ide-completion/src/tests/item.rs +++ b/crates/ide-completion/src/tests/item.rs @@ -2,20 +2,13 @@ //! //! Except for use items which are tested in [super::use_tree] and mod declarations with are tested //! in [crate::completions::mod_]. -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::{completion_list, BASE_ITEMS_FIXTURE}; - -use super::check_edit; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); - expect.assert_eq(&actual) -} +use crate::tests::{check_edit, check_with_base_items}; #[test] fn target_type_or_trait_in_impl_block() { - check( + check_with_base_items( r#" impl Tra$0 "#, @@ -37,7 +30,7 @@ impl Tra$0 #[test] fn target_type_in_trait_impl_block() { - check( + check_with_base_items( r#" impl Trait for Str$0 "#, @@ -59,7 +52,7 @@ impl Trait for Str$0 #[test] fn after_trait_name_in_trait_def() { - check( + check_with_base_items( r"trait A $0", expect![[r#" kw where @@ -69,21 +62,21 @@ fn after_trait_name_in_trait_def() { #[test] fn after_target_name_in_impl() { - check( + check_with_base_items( r"impl Trait $0", expect![[r#" kw for kw where "#]], ); - check( + check_with_base_items( r"impl Trait f$0", expect![[r#" kw for kw where "#]], ); - check( + check_with_base_items( r"impl Trait for Type $0", expect![[r#" kw where @@ -93,44 +86,44 @@ fn after_target_name_in_impl() { #[test] fn completes_where() { - check( + check_with_base_items( r"struct Struct $0", expect![[r#" kw where "#]], ); - check( + check_with_base_items( r"struct Struct $0 {}", expect![[r#" kw where "#]], ); // FIXME: This shouldn't be completed here - check( + check_with_base_items( r"struct Struct $0 ()", expect![[r#" kw where "#]], ); - check( + check_with_base_items( r"fn func() $0", expect![[r#" kw where "#]], ); - check( + check_with_base_items( r"enum Enum $0", expect![[r#" kw where "#]], ); - check( + check_with_base_items( r"enum Enum $0 {}", expect![[r#" kw where "#]], ); - check( + check_with_base_items( r"trait Trait $0 {}", expect![[r#" kw where @@ -140,7 +133,7 @@ fn completes_where() { #[test] fn before_record_field() { - check( + check_with_base_items( r#" struct Foo { $0 @@ -244,7 +237,7 @@ impl Copy for S where $0 #[test] fn test_is_not_considered_macro() { - check( + check_with_base_items( r#" #[rustc_builtin] pub macro test($item:item) { diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs index d3d52dc6df..841c42123a 100644 --- a/crates/ide-completion/src/tests/item_list.rs +++ b/crates/ide-completion/src/tests/item_list.rs @@ -1,16 +1,11 @@ //! Completion tests for item list position. -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); - expect.assert_eq(&actual) -} +use crate::tests::{check, check_edit, check_with_base_items}; #[test] fn in_mod_item_list() { - check( + check_with_base_items( r#"mod tests { $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -43,7 +38,7 @@ fn in_mod_item_list() { #[test] fn in_source_file_item_list() { - check( + check_with_base_items( r#"$0"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -76,7 +71,7 @@ fn in_source_file_item_list() { #[test] fn in_item_list_after_attr() { - check( + check_with_base_items( r#"#[attr] $0"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -109,7 +104,7 @@ fn in_item_list_after_attr() { #[test] fn in_qualified_path() { - check( + check_with_base_items( r#"crate::$0"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -120,7 +115,7 @@ fn in_qualified_path() { #[test] fn after_unsafe_token() { - check( + check_with_base_items( r#"unsafe $0"#, expect![[r#" kw async @@ -134,7 +129,7 @@ fn after_unsafe_token() { #[test] fn after_async_token() { - check( + check_with_base_items( r#"async $0"#, expect![[r#" kw fn @@ -145,7 +140,7 @@ fn after_async_token() { #[test] fn after_visibility() { - check( + check_with_base_items( r#"pub $0"#, expect![[r#" kw async @@ -167,7 +162,7 @@ fn after_visibility() { #[test] fn after_visibility_unsafe() { - check( + check_with_base_items( r#"pub unsafe $0"#, expect![[r#" kw async @@ -179,7 +174,7 @@ fn after_visibility_unsafe() { #[test] fn in_impl_assoc_item_list() { - check( + check_with_base_items( r#"impl Struct { $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -199,7 +194,7 @@ fn in_impl_assoc_item_list() { #[test] fn in_impl_assoc_item_list_after_attr() { - check( + check_with_base_items( r#"impl Struct { #[attr] $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -219,7 +214,7 @@ fn in_impl_assoc_item_list_after_attr() { #[test] fn in_trait_assoc_item_list() { - check( + check_with_base_items( r"trait Foo { $0 }", expect![[r#" ma makro!(…) macro_rules! makro @@ -237,7 +232,7 @@ fn in_trait_assoc_item_list() { #[test] fn in_trait_assoc_fn_missing_body() { - check( + check_with_base_items( r#"trait Foo { fn function(); $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -255,7 +250,7 @@ fn in_trait_assoc_fn_missing_body() { #[test] fn in_trait_assoc_const_missing_body() { - check( + check_with_base_items( r#"trait Foo { const CONST: (); $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -273,7 +268,7 @@ fn in_trait_assoc_const_missing_body() { #[test] fn in_trait_assoc_type_aliases_missing_ty() { - check( + check_with_base_items( r#"trait Foo { type Type; $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -291,7 +286,7 @@ fn in_trait_assoc_type_aliases_missing_ty() { #[test] fn in_trait_impl_assoc_item_list() { - check( + check_with_base_items( r#" trait Test { type Type0; @@ -326,7 +321,7 @@ impl Test for () { #[test] fn in_trait_impl_no_unstable_item_on_stable() { - check_empty( + check( r#" trait Test { #[unstable] @@ -350,7 +345,7 @@ impl Test for () { #[test] fn in_trait_impl_unstable_item_on_nightly() { - check_empty( + check( r#" //- toolchain:nightly trait Test { @@ -378,7 +373,7 @@ impl Test for () { #[test] fn after_unit_struct() { - check( + check_with_base_items( r#"struct S; f$0"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -500,7 +495,7 @@ type O = $0; #[test] fn inside_extern_blocks() { // Should suggest `fn`, `static`, `unsafe` - check( + check_with_base_items( r#"extern { $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -517,7 +512,7 @@ fn inside_extern_blocks() { ); // Should suggest `fn`, `static`, `safe`, `unsafe` - check( + check_with_base_items( r#"unsafe extern { $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro @@ -534,7 +529,7 @@ fn inside_extern_blocks() { "#]], ); - check( + check_with_base_items( r#"unsafe extern { pub safe $0 }"#, expect![[r#" kw fn @@ -542,7 +537,7 @@ fn inside_extern_blocks() { "#]], ); - check( + check_with_base_items( r#"unsafe extern { pub unsafe $0 }"#, expect![[r#" kw fn diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 2f1f555e52..9ec27252fd 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -1,16 +1,11 @@ //! Completion tests for pattern position. -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}")); - expect.assert_eq(&actual) -} +use crate::tests::{check, check_edit, check_with_base_items}; #[test] fn wildcard() { - check( + check_with_base_items( r#" fn quux() { let _$0 @@ -22,7 +17,7 @@ fn quux() { #[test] fn ident_rebind_pat() { - check_empty( + check( r#" fn quux() { let en$0 @ x @@ -37,7 +32,7 @@ fn quux() { #[test] fn ident_ref_pat() { - check_empty( + check( r#" fn quux() { let ref en$0 @@ -47,7 +42,7 @@ fn quux() { kw mut "#]], ); - check_empty( + check( r#" fn quux() { let ref en$0 @ x @@ -61,7 +56,7 @@ fn quux() { #[test] fn ident_ref_mut_pat() { - check_empty( + check( r#" fn quux() { let ref mut en$0 @@ -69,7 +64,7 @@ fn quux() { "#, expect![[r#""#]], ); - check_empty( + check( r#" fn quux() { let ref mut en$0 @ x @@ -81,7 +76,7 @@ fn quux() { #[test] fn ref_pat() { - check_empty( + check( r#" fn quux() { let &en$0 @@ -91,7 +86,7 @@ fn quux() { kw mut "#]], ); - check_empty( + check( r#" fn quux() { let &mut en$0 @@ -99,7 +94,7 @@ fn quux() { "#, expect![[r#""#]], ); - check_empty( + check( r#" fn foo() { for &$0 in () {} @@ -113,7 +108,7 @@ fn foo() { #[test] fn refutable() { - check( + check_with_base_items( r#" fn foo() { if let a$0 @@ -139,7 +134,7 @@ fn foo() { #[test] fn irrefutable() { - check( + check_with_base_items( r#" enum SingleVariantEnum { Variant @@ -168,7 +163,7 @@ fn foo() { #[test] fn in_param() { - check( + check_with_base_items( r#" fn foo(a$0) { } @@ -185,7 +180,7 @@ fn foo(a$0) { kw ref "#]], ); - check( + check_with_base_items( r#" fn foo(a$0: Tuple) { } @@ -207,7 +202,7 @@ fn foo(a$0: Tuple) { #[test] fn only_fn_like_macros() { - check_empty( + check( r#" macro_rules! m { ($e:expr) => { $e } } @@ -228,7 +223,7 @@ fn foo() { #[test] fn in_simple_macro_call() { - check_empty( + check( r#" macro_rules! m { ($e:expr) => { $e } } enum E { X } @@ -249,7 +244,7 @@ fn foo() { #[test] fn omits_private_fields_pat() { - check_empty( + check( r#" mod foo { pub struct Record { pub field: i32, _field: i32 } @@ -277,7 +272,7 @@ fn outer() { #[test] fn completes_self_pats() { - check_empty( + check( r#" struct Foo(i32); impl Foo { @@ -301,7 +296,7 @@ impl Foo { #[test] fn enum_qualified() { - check( + check_with_base_items( r#" impl Enum { type AssocType = (); @@ -323,7 +318,7 @@ fn func() { #[test] fn completes_in_record_field_pat() { - check_empty( + check( r#" struct Foo { bar: Bar } struct Bar(u32); @@ -342,7 +337,7 @@ fn outer(Foo { bar: $0 }: Foo) {} #[test] fn skips_in_record_field_pat_name() { - check_empty( + check( r#" struct Foo { bar: Bar } struct Bar(u32); @@ -357,7 +352,7 @@ fn outer(Foo { bar$0 }: Foo) {} #[test] fn completes_in_record_field_pat_with_generic_type_alias() { - check_empty( + check( r#" type Wrap = T; @@ -386,7 +381,7 @@ fn main() { #[test] fn completes_in_fn_param() { - check_empty( + check( r#" struct Foo { bar: Bar } struct Bar(u32); @@ -405,7 +400,7 @@ fn foo($0) {} #[test] fn completes_in_closure_param() { - check_empty( + check( r#" struct Foo { bar: Bar } struct Bar(u32); @@ -426,7 +421,7 @@ fn foo() { #[test] fn completes_no_delims_if_existing() { - check_empty( + check( r#" struct Bar(u32); fn foo() { @@ -441,7 +436,7 @@ fn foo() { kw self:: "#]], ); - check_empty( + check( r#" struct Foo { bar: u32 } fn foo() { @@ -456,7 +451,7 @@ fn foo() { kw self:: "#]], ); - check_empty( + check( r#" enum Enum { TupleVariant(u32) @@ -471,7 +466,7 @@ fn foo() { bn TupleVariant TupleVariant "#]], ); - check_empty( + check( r#" enum Enum { RecordVariant { field: u32 } @@ -519,7 +514,7 @@ fn foo() { #[test] fn completes_enum_variant_pat_escape() { cov_mark::check!(enum_variant_pattern_path); - check_empty( + check( r#" enum Enum { A, @@ -544,7 +539,7 @@ fn foo() { "#]], ); - check_empty( + check( r#" enum Enum { A, @@ -569,7 +564,7 @@ fn foo() { #[test] fn completes_associated_const() { - check_empty( + check( r#" #[derive(PartialEq, Eq)] struct Ty(u8); @@ -590,7 +585,7 @@ fn f(t: Ty) { "#]], ); - check_empty( + check( r#" enum MyEnum {} @@ -612,7 +607,7 @@ fn f(e: MyEnum) { "#]], ); - check_empty( + check( r#" union U { i: i32, @@ -637,7 +632,7 @@ fn f(u: U) { "#]], ); - check_empty( + check( r#" #![rustc_coherence_is_core] #[lang = "u32"] @@ -659,7 +654,7 @@ fn f(v: u32) { #[test] fn in_method_param() { - check_empty( + check( r#" struct Ty(u8); @@ -680,7 +675,7 @@ impl Ty { kw ref "#]], ); - check_empty( + check( r#" struct Ty(u8); @@ -701,7 +696,7 @@ impl Ty { kw ref "#]], ); - check_empty( + check( r#" struct Ty(u8); @@ -722,7 +717,7 @@ impl Ty { kw ref "#]], ); - check_empty( + check( r#" struct Ty(u8); @@ -743,7 +738,7 @@ impl Ty { #[test] fn through_alias() { - check_empty( + check( r#" enum Enum { Unit, @@ -770,7 +765,7 @@ fn f(x: EnumAlias) { #[test] fn pat_no_unstable_item_on_stable() { - check_empty( + check( r#" //- /main.rs crate:main deps:std use std::*; @@ -795,7 +790,7 @@ pub enum Enum { #[test] fn pat_unstable_item_on_nightly() { - check_empty( + check( r#" //- toolchain:nightly //- /main.rs crate:main deps:std @@ -908,7 +903,7 @@ fn foo() { ); // Do not suggest reserved keywords - check_empty( + check( r#" struct Struct; @@ -926,7 +921,7 @@ fn foo() { #[test] fn private_item_in_module_in_function_body() { - check_empty( + check( r#" fn main() { mod foo { diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs index c1926359ef..65036f6a22 100644 --- a/crates/ide-completion/src/tests/predicate.rs +++ b/crates/ide-completion/src/tests/predicate.rs @@ -1,17 +1,12 @@ //! Completion tests for predicates and bounds. -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE}; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}")); - expect.assert_eq(&actual) -} +use crate::tests::{check, check_with_base_items}; #[test] fn predicate_start() { // FIXME: `for` kw - check( + check_with_base_items( r#" struct Foo<'lt, T, const C: usize> where $0 {} "#, @@ -34,7 +29,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} #[test] fn bound_for_type_pred() { - check( + check_with_base_items( r#" struct Foo<'lt, T, const C: usize> where T: $0 {} "#, @@ -52,7 +47,7 @@ struct Foo<'lt, T, const C: usize> where T: $0 {} fn bound_for_lifetime_pred() { // FIXME: should only show lifetimes here, that is we shouldn't get any completions here when not typing // a `'` - check( + check_with_base_items( r#" struct Foo<'lt, T, const C: usize> where 'lt: $0 {} "#, @@ -68,7 +63,7 @@ struct Foo<'lt, T, const C: usize> where 'lt: $0 {} #[test] fn bound_for_for_pred() { - check( + check_with_base_items( r#" struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {} "#, @@ -84,7 +79,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {} #[test] fn param_list_for_for_pred() { - check( + check_with_base_items( r#" struct Foo<'lt, T, const C: usize> where for<'a> $0 {} "#, @@ -107,7 +102,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} #[test] fn pred_on_fn_in_impl() { - check( + check_with_base_items( r#" impl Record { fn method(self) where $0 {} @@ -132,7 +127,7 @@ impl Record { #[test] fn pred_no_unstable_item_on_stable() { - check_empty( + check( r#" //- /main.rs crate:main deps:std use std::*; @@ -151,7 +146,7 @@ pub trait Trait {} #[test] fn pred_unstable_item_on_nightly() { - check_empty( + check( r#" //- toolchain:nightly //- /main.rs crate:main deps:std diff --git a/crates/ide-completion/src/tests/proc_macros.rs b/crates/ide-completion/src/tests/proc_macros.rs index afc286b6fb..6b1dfe366c 100644 --- a/crates/ide-completion/src/tests/proc_macros.rs +++ b/crates/ide-completion/src/tests/proc_macros.rs @@ -1,12 +1,7 @@ //! Completion tests for expressions. -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::completion_list; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual) -} +use crate::tests::check; #[test] fn complete_dot_in_attr() { diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index a9c9f604e0..a1013b8654 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -1,14 +1,9 @@ -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::completion_list; +use crate::tests::check; use super::check_edit; -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual); -} - #[test] fn without_default_impl() { check( diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 6cfb2231a9..2b05184bdb 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -5,32 +5,12 @@ use ide_db::SymbolKind; use crate::{ tests::{ - check_edit, completion_list, completion_list_no_kw, completion_list_with_trigger_character, + check, check_edit, check_no_kw, check_with_trigger_character, do_completion_with_config, + TEST_CONFIG, }, CompletionItemKind, }; -use super::{do_completion_with_config, TEST_CONFIG}; - -fn check_no_kw(ra_fixture: &str, expect: Expect) { - let actual = completion_list_no_kw(ra_fixture); - expect.assert_eq(&actual) -} - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual) -} - -pub(crate) fn check_with_trigger_character( - ra_fixture: &str, - trigger_character: Option, - expect: Expect, -) { - let actual = completion_list_with_trigger_character(ra_fixture, trigger_character); - expect.assert_eq(&actual) -} - #[test] fn completes_if_prefix_is_keyword() { check_edit( diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 9ea262bcc5..c7e2d05825 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -1,16 +1,11 @@ //! Completion tests for type position. -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE}; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}")); - expect.assert_eq(&actual) -} +use crate::tests::{check, check_with_base_items}; #[test] fn record_field_ty() { - check( + check_with_base_items( r#" struct Foo<'lt, T, const C: usize> { f: $0 @@ -37,7 +32,7 @@ struct Foo<'lt, T, const C: usize> { #[test] fn tuple_struct_field() { - check( + check_with_base_items( r#" struct Foo<'lt, T, const C: usize>(f$0); "#, @@ -65,7 +60,7 @@ struct Foo<'lt, T, const C: usize>(f$0); #[test] fn fn_return_type() { - check( + check_with_base_items( r#" fn x<'lt, T, const C: usize>() -> $0 "#, @@ -88,7 +83,7 @@ fn x<'lt, T, const C: usize>() -> $0 #[test] fn fn_return_type_no_local_items() { - check( + check_with_base_items( r#" fn foo() -> B$0 { struct Bar; @@ -118,7 +113,7 @@ fn foo() -> B$0 { #[test] fn inferred_type_const() { - check( + check_with_base_items( r#" struct Foo(T); const FOO: $0 = Foo(2); @@ -143,7 +138,7 @@ const FOO: $0 = Foo(2); #[test] fn inferred_type_closure_param() { - check( + check_with_base_items( r#" fn f1(f: fn(i32) -> i32) {} fn f2() { @@ -169,7 +164,7 @@ fn f2() { #[test] fn inferred_type_closure_return() { - check( + check_with_base_items( r#" fn f1(f: fn(u64) -> u64) {} fn f2() { @@ -197,7 +192,7 @@ fn f2() { #[test] fn inferred_type_fn_return() { - check( + check_with_base_items( r#" fn f2(x: u64) -> $0 { x + 5 @@ -222,7 +217,7 @@ fn f2(x: u64) -> $0 { #[test] fn inferred_type_fn_param() { - check( + check_with_base_items( r#" fn f1(x: i32) {} fn f2(x: $0) { @@ -248,7 +243,7 @@ fn f2(x: $0) { #[test] fn inferred_type_not_in_the_scope() { - check( + check_with_base_items( r#" mod a { pub struct Foo(T); @@ -282,7 +277,7 @@ fn foo<'lt, T, const C: usize>() { #[test] fn inferred_type_let() { - check( + check_with_base_items( r#" struct Foo(T); fn foo<'lt, T, const C: usize>() { @@ -311,7 +306,7 @@ fn foo<'lt, T, const C: usize>() { #[test] fn body_type_pos() { - check( + check_with_base_items( r#" fn foo<'lt, T, const C: usize>() { let local = (); @@ -333,7 +328,7 @@ fn foo<'lt, T, const C: usize>() { kw self:: "#]], ); - check( + check_with_base_items( r#" fn foo<'lt, T, const C: usize>() { let local = (); @@ -356,7 +351,7 @@ fn foo<'lt, T, const C: usize>() { #[test] fn completes_types_and_const_in_arg_list() { cov_mark::check!(complete_assoc_type_in_generics_list); - check( + check_with_base_items( r#" trait Trait1 { type Super; @@ -372,7 +367,7 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} ta Super = (as Trait1) type Super "#]], ); - check( + check_with_base_items( r#" trait Trait1 { type Super; @@ -400,7 +395,7 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} kw self:: "#]], ); - check( + check_with_base_items( r#" trait Trait2 { type Foo; @@ -424,7 +419,7 @@ fn foo<'lt, T: Trait2, const CONST_PARAM: usize>(_: T) {} #[test] fn no_assoc_completion_outside_type_bounds() { - check( + check_with_base_items( r#" struct S; trait Tr { @@ -454,7 +449,7 @@ impl Tr<$0 #[test] fn enum_qualified() { - check( + check_with_base_items( r#" impl Enum { type AssocType = (); @@ -471,7 +466,7 @@ fn func(_: Enum::$0) {} #[test] fn completes_type_parameter_or_associated_type() { - check( + check_with_base_items( r#" trait MyTrait { type Item1; @@ -496,7 +491,7 @@ fn f(t: impl MyTrait { type Item1; @@ -521,7 +516,7 @@ fn f(t: impl MyTrait { type Item1; @@ -539,7 +534,7 @@ fn f(t: impl MyTrait { type Item1; @@ -564,7 +559,7 @@ fn f(t: impl MyTrait { type Item1; @@ -591,7 +586,7 @@ fn f(t: impl MyTrait { type Item1; @@ -609,7 +604,7 @@ fn f(t: impl MyTrait()`, but we currently don't suggest `usize` in constant position. - check( + check_with_base_items( r#" struct Foo; const X: usize = 0; @@ -775,7 +770,7 @@ fn completes_const_and_type_generics_separately() { ); // Method generic params - check( + check_with_base_items( r#" const X: usize = 0; struct Foo; @@ -799,7 +794,7 @@ fn completes_const_and_type_generics_separately() { kw self:: "#]], ); - check( + check_with_base_items( r#" const X: usize = 0; struct Foo; @@ -818,7 +813,7 @@ fn completes_const_and_type_generics_separately() { ); // Associated type generic params - check( + check_with_base_items( r#" const X: usize = 0; struct Foo; @@ -843,7 +838,7 @@ fn completes_const_and_type_generics_separately() { kw self:: "#]], ); - check( + check_with_base_items( r#" const X: usize = 0; struct Foo; @@ -862,7 +857,7 @@ fn completes_const_and_type_generics_separately() { ); // Type generic params - check( + check_with_base_items( r#" const X: usize = 0; struct Foo(T); @@ -880,7 +875,7 @@ fn completes_const_and_type_generics_separately() { ); // Type alias generic params - check( + check_with_base_items( r#" const X: usize = 0; struct Foo(T); @@ -899,7 +894,7 @@ fn completes_const_and_type_generics_separately() { ); // Enum variant params - check( + check_with_base_items( r#" const X: usize = 0; enum Foo { A(T), B } @@ -917,7 +912,7 @@ fn completes_const_and_type_generics_separately() { ); // Trait params - check( + check_with_base_items( r#" const X: usize = 0; trait Foo {} @@ -933,7 +928,7 @@ fn completes_const_and_type_generics_separately() { ); // Trait alias params - check( + check_with_base_items( r#" #![feature(trait_alias)] const X: usize = 0; @@ -951,7 +946,7 @@ fn completes_const_and_type_generics_separately() { ); // Omitted lifetime params - check( + check_with_base_items( r#" struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); fn foo<'a>() { S::; } @@ -964,7 +959,7 @@ fn foo<'a>() { S::; } "#]], ); // Explicit lifetime params - check( + check_with_base_items( r#" struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); fn foo<'a>() { S::<'static, 'static, F$0, _>; } @@ -976,7 +971,7 @@ fn foo<'a>() { S::<'static, 'static, F$0, _>; } kw self:: "#]], ); - check( + check_with_base_items( r#" struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); fn foo<'a>() { S::<'static, F$0, _, _>; } @@ -992,7 +987,7 @@ fn foo<'a>() { S::<'static, F$0, _, _>; } #[test] fn complete_traits_on_impl_trait_block() { - check( + check_with_base_items( r#" trait Foo {} @@ -1012,7 +1007,7 @@ impl $0 for Bar { } #[test] fn complete_traits_with_path_on_impl_trait_block() { - check( + check_with_base_items( r#" mod outer { pub trait Foo {} diff --git a/crates/ide-completion/src/tests/use_tree.rs b/crates/ide-completion/src/tests/use_tree.rs index 2ea2e4e4c9..04b3a47a64 100644 --- a/crates/ide-completion/src/tests/use_tree.rs +++ b/crates/ide-completion/src/tests/use_tree.rs @@ -1,12 +1,7 @@ //! Completion tests for use trees. -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::completion_list; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual) -} +use crate::tests::check; #[test] fn use_tree_completion() { diff --git a/crates/ide-completion/src/tests/visibility.rs b/crates/ide-completion/src/tests/visibility.rs index c18d6e66dd..4b5a0ac1c2 100644 --- a/crates/ide-completion/src/tests/visibility.rs +++ b/crates/ide-completion/src/tests/visibility.rs @@ -1,17 +1,7 @@ //! Completion tests for visibility modifiers. -use expect_test::{expect, Expect}; +use expect_test::expect; -use crate::tests::{completion_list, completion_list_with_trigger_character}; - -fn check(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual) -} - -fn check_with_trigger_character(ra_fixture: &str, trigger_character: char, expect: Expect) { - let actual = completion_list_with_trigger_character(ra_fixture, Some(trigger_character)); - expect.assert_eq(&actual) -} +use crate::tests::{check, check_with_trigger_character}; #[test] fn empty_pub() { @@ -20,7 +10,7 @@ fn empty_pub() { r#" pub($0) "#, - '(', + Some('('), expect![[r#" kw crate kw in From 1f9686993a3b3b8b01ebfd9c07007d618223632a Mon Sep 17 00:00:00 2001 From: Nicholas Rishel Date: Tue, 7 Jan 2025 18:40:46 -0800 Subject: [PATCH 21/98] Add config setting which allows adding additional include paths to the VFS. --- crates/project-model/src/cargo_workspace.rs | 2 ++ crates/project-model/src/tests.rs | 3 ++ crates/project-model/src/workspace.rs | 32 +++++++++++++++++++-- crates/rust-analyzer/src/cli/rustc_tests.rs | 1 + crates/rust-analyzer/src/config.rs | 12 ++++++++ docs/user/generated_config.adoc | 7 +++++ editors/code/package.json | 13 +++++++++ 7 files changed, 67 insertions(+), 3 deletions(-) diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 4d906c2aeb..e4a6113462 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -92,6 +92,8 @@ pub struct CargoConfig { pub sysroot_src: Option, /// rustc private crate source pub rustc_source: Option, + /// Extra includes to add to the VFS. + pub extra_includes: Vec, pub cfg_overrides: CfgOverrides, /// Invoke `cargo check` through the RUSTC_WRAPPER. pub wrap_rustc_in_build_scripts: bool, diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 681bce3a5a..f111383112 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -49,6 +49,7 @@ fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace { rustc_cfg: Vec::new(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), + extra_includes: Vec::new(), } } @@ -63,6 +64,7 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { toolchain: None, target_layout: Err(Arc::from("test has no data layout")), cfg_overrides: Default::default(), + extra_includes: Vec::new(), }; to_crate_graph(project_workspace, &mut Default::default()) } @@ -284,6 +286,7 @@ fn smoke_test_real_sysroot_cargo() { cfg_overrides: Default::default(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), + extra_includes: Vec::new(), }; project_workspace.to_crate_graph( &mut { diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index a345c6bcce..465a1ad0ec 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -63,6 +63,8 @@ pub struct ProjectWorkspace { pub target_layout: TargetLayoutLoadResult, /// A set of cfg overrides for this workspace. pub cfg_overrides: CfgOverrides, + /// Additional includes to add for the VFS. + pub extra_includes: Vec, } #[derive(Clone)] @@ -104,7 +106,15 @@ pub enum ProjectWorkspaceKind { impl fmt::Debug for ProjectWorkspace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Make sure this isn't too verbose. - let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides } = self; + let Self { + kind, + sysroot, + rustc_cfg, + toolchain, + target_layout, + cfg_overrides, + extra_includes, + } = self; match kind { ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc, set_test } => f .debug_struct("Cargo") @@ -117,6 +127,7 @@ impl fmt::Debug for ProjectWorkspace { ) .field("n_rustc_cfg", &rustc_cfg.len()) .field("n_cfg_overrides", &cfg_overrides.len()) + .field("n_extra_includes", &extra_includes.len()) .field("toolchain", &toolchain) .field("data_layout", &target_layout) .field("set_test", set_test) @@ -130,7 +141,8 @@ impl fmt::Debug for ProjectWorkspace { .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) .field("data_layout", &target_layout) - .field("n_cfg_overrides", &cfg_overrides.len()); + .field("n_cfg_overrides", &cfg_overrides.len()) + .field("n_extra_includes", &extra_includes.len()); debug_struct.finish() } @@ -144,6 +156,7 @@ impl fmt::Debug for ProjectWorkspace { .field("toolchain", &toolchain) .field("data_layout", &target_layout) .field("n_cfg_overrides", &cfg_overrides.len()) + .field("n_extra_includes", &extra_includes.len()) .field("set_test", set_test) .finish(), } @@ -320,6 +333,7 @@ impl ProjectWorkspace { cfg_overrides, toolchain, target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + extra_includes: config.extra_includes.clone(), }) } @@ -340,6 +354,7 @@ impl ProjectWorkspace { toolchain, target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), cfg_overrides: config.cfg_overrides.clone(), + extra_includes: config.extra_includes.clone(), } } @@ -399,6 +414,7 @@ impl ProjectWorkspace { toolchain, target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), cfg_overrides: config.cfg_overrides.clone(), + extra_includes: config.extra_includes.clone(), }) } @@ -565,7 +581,13 @@ impl ProjectWorkspace { PackageRoot { is_local: krate.is_workspace_member, - include: krate.include.iter().cloned().chain(build_file).collect(), + include: krate + .include + .iter() + .cloned() + .chain(build_file) + .chain(self.extra_includes.iter().cloned()) + .collect(), exclude: krate.exclude.clone(), } }) @@ -603,6 +625,8 @@ impl ProjectWorkspace { let mut exclude = vec![pkg_root.join(".git")]; if is_local { + include.extend(self.extra_includes.iter().cloned()); + exclude.push(pkg_root.join("target")); } else { exclude.push(pkg_root.join("tests")); @@ -661,6 +685,8 @@ impl ProjectWorkspace { let mut exclude = vec![pkg_root.join(".git")]; if is_local { + include.extend(self.extra_includes.iter().cloned()); + exclude.push(pkg_root.join("target")); } else { exclude.push(pkg_root.join("tests")); diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index 6b0ce4db7c..199f61e70f 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -93,6 +93,7 @@ impl Tester { toolchain: None, target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), cfg_overrides: Default::default(), + extra_includes: vec![], }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: false, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 30f0031905..28afd83e5c 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -728,6 +728,10 @@ config_data! { /// available on a nightly build. rustfmt_rangeFormatting_enable: bool = false, + /// Additional paths to include in the VFS. Generally for code that is + /// generated or otherwise managed by a build system outside of Cargo, + /// though Cargo might be the eventual consumer. + vfs_extraIncludes: Vec = vec![], /// Workspace symbol search kind. workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes, @@ -1926,6 +1930,13 @@ impl Config { }); let sysroot_src = self.cargo_sysrootSrc(source_root).as_ref().map(|sysroot| self.root_path.join(sysroot)); + let extra_includes = self + .vfs_extraIncludes(source_root) + .iter() + .map(String::as_str) + .map(AbsPathBuf::try_from) + .filter_map(Result::ok) + .collect(); CargoConfig { all_targets: *self.cargo_allTargets(source_root), @@ -1940,6 +1951,7 @@ impl Config { sysroot, sysroot_src, rustc_source, + extra_includes, cfg_overrides: project_model::CfgOverrides { global: CfgDiff::new( self.cargo_cfgs(source_root) diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 5b86766aa8..b3d3614e22 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -1051,6 +1051,13 @@ Show documentation. -- Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`. -- +[[rust-analyzer.vfs.extraIncludes]]rust-analyzer.vfs.extraIncludes (default: `[]`):: ++ +-- +Additional paths to include in the VFS. Generally for code that is +generated or otherwise managed by a build system outside of Cargo, +though Cargo might be the eventual consumer. +-- [[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 80246bf3fe..87afc5c281 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -2718,6 +2718,19 @@ } } }, + { + "title": "vfs", + "properties": { + "rust-analyzer.vfs.extraIncludes": { + "markdownDescription": "Additional paths to include in the VFS. Generally for code that is\ngenerated or otherwise managed by a build system outside of Cargo,\nthough Cargo might be the eventual consumer.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, { "title": "workspace", "properties": { From 651b43e5511a6c3938bb84462ab8b56dd8217990 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Sat, 16 Nov 2024 14:42:17 -0500 Subject: [PATCH 22/98] internal: Migrate `wrap_return_type` assist to use `SyntaxEditor` --- .../src/handlers/add_turbo_fish.rs | 2 +- .../src/handlers/wrap_return_type.rs | 119 +++++++++--------- .../src/ast/syntax_factory/constructors.rs | 110 +++++++++++++++- 3 files changed, 168 insertions(+), 63 deletions(-) diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs index 62700ab180..04d63f5bc8 100644 --- a/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -189,7 +189,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti /// This will create a turbofish generic arg list corresponding to the number of arguments fn get_fish_head(make: &SyntaxFactory, number_of_arguments: usize) -> ast::GenericArgList { let args = (0..number_of_arguments).map(|_| make::type_arg(make::ty_placeholder()).into()); - make.turbofish_generic_arg_list(args) + make.generic_arg_list(args, true) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/wrap_return_type.rs b/crates/ide-assists/src/handlers/wrap_return_type.rs index 658600cd2d..0b145dcb06 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -6,10 +6,9 @@ use ide_db::{ famous_defs::FamousDefs, syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, }; -use itertools::Itertools; use syntax::{ - ast::{self, make, Expr, HasGenericParams}, - match_ast, ted, AstNode, ToSmolStr, + ast::{self, syntax_factory::SyntaxFactory, Expr, HasGenericArgs, HasGenericParams}, + match_ast, AstNode, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -43,11 +42,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let ret_type = ctx.find_node_at_offset::()?; let parent = ret_type.syntax().parent()?; - let body = match_ast! { + let body_expr = match_ast! { match parent { - ast::Fn(func) => func.body()?, + ast::Fn(func) => func.body()?.into(), ast::ClosureExpr(closure) => match closure.body()? { - Expr::BlockExpr(block) => block, + Expr::BlockExpr(block) => block.into(), // closures require a block when a return type is specified _ => return None, }, @@ -75,56 +74,65 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op kind.assist_id(), kind.label(), type_ref.syntax().text_range(), - |edit| { - let alias = wrapper_alias(ctx, &core_wrapper, type_ref, kind.symbol()); - let new_return_ty = - alias.unwrap_or_else(|| kind.wrap_type(type_ref)).clone_for_update(); - - let body = edit.make_mut(ast::Expr::BlockExpr(body.clone())); + |builder| { + let mut editor = builder.make_editor(&parent); + let make = SyntaxFactory::new(); + let alias = wrapper_alias(ctx, &make, &core_wrapper, type_ref, kind.symbol()); + let new_return_ty = alias.unwrap_or_else(|| match kind { + WrapperKind::Option => make.ty_option(type_ref.clone()), + WrapperKind::Result => make.ty_result(type_ref.clone(), make.ty_infer().into()), + }); let mut exprs_to_wrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); - walk_expr(&body, &mut |expr| { + walk_expr(&body_expr, &mut |expr| { if let Expr::ReturnExpr(ret_expr) = expr { if let Some(ret_expr_arg) = &ret_expr.expr() { for_each_tail_expr(ret_expr_arg, tail_cb); } } }); - for_each_tail_expr(&body, tail_cb); + for_each_tail_expr(&body_expr, tail_cb); for ret_expr_arg in exprs_to_wrap { - let happy_wrapped = make::expr_call( - make::expr_path(make::ext::ident_path(kind.happy_ident())), - make::arg_list(iter::once(ret_expr_arg.clone())), - ) - .clone_for_update(); - ted::replace(ret_expr_arg.syntax(), happy_wrapped.syntax()); + let happy_wrapped = make.expr_call( + make.expr_path(make.ident_path(kind.happy_ident())), + make.arg_list(iter::once(ret_expr_arg.clone())), + ); + editor.replace(ret_expr_arg.syntax(), happy_wrapped.syntax()); } - let old_return_ty = edit.make_mut(type_ref.clone()); - ted::replace(old_return_ty.syntax(), new_return_ty.syntax()); + editor.replace(type_ref.syntax(), new_return_ty.syntax()); if let WrapperKind::Result = kind { // Add a placeholder snippet at the first generic argument that doesn't equal the return type. // This is normally the error type, but that may not be the case when we inserted a type alias. - let args = - new_return_ty.syntax().descendants().find_map(ast::GenericArgList::cast); - let error_type_arg = args.and_then(|list| { - list.generic_args().find(|arg| match arg { - ast::GenericArg::TypeArg(_) => { - arg.syntax().text() != type_ref.syntax().text() - } - ast::GenericArg::LifetimeArg(_) => false, - _ => true, - }) + let args = new_return_ty + .path() + .unwrap() + .segment() + .unwrap() + .generic_arg_list() + .unwrap(); + let error_type_arg = args.generic_args().find(|arg| match arg { + ast::GenericArg::TypeArg(_) => { + arg.syntax().text() != type_ref.syntax().text() + } + ast::GenericArg::LifetimeArg(_) => false, + _ => true, }); if let Some(error_type_arg) = error_type_arg { if let Some(cap) = ctx.config.snippet_cap { - edit.add_placeholder_snippet(cap, error_type_arg); + editor.add_annotation( + error_type_arg.syntax(), + builder.make_placeholder_snippet(cap), + ); } } } + + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.file_id(), editor); }, ); } @@ -176,22 +184,16 @@ impl WrapperKind { WrapperKind::Result => hir::sym::Result.clone(), } } - - fn wrap_type(&self, type_ref: &ast::Type) -> ast::Type { - match self { - WrapperKind::Option => make::ext::ty_option(type_ref.clone()), - WrapperKind::Result => make::ext::ty_result(type_ref.clone(), make::ty_placeholder()), - } - } } // Try to find an wrapper type alias in the current scope (shadowing the default). fn wrapper_alias( ctx: &AssistContext<'_>, + make: &SyntaxFactory, core_wrapper: &hir::Enum, ret_type: &ast::Type, wrapper: hir::Symbol, -) -> Option { +) -> Option { let wrapper_path = hir::ModPath::from_segments( hir::PathKind::Plain, iter::once(hir::Name::new_symbol_root(wrapper)), @@ -207,25 +209,28 @@ fn wrapper_alias( }) .find_map(|alias| { let mut inserted_ret_type = false; - let generic_params = alias - .source(ctx.db())? - .value - .generic_param_list()? - .generic_params() - .map(|param| match param { - // Replace the very first type parameter with the functions return type. - ast::GenericParam::TypeParam(_) if !inserted_ret_type => { - inserted_ret_type = true; - ret_type.to_smolstr() + let generic_args = + alias.source(ctx.db())?.value.generic_param_list()?.generic_params().map(|param| { + match param { + // Replace the very first type parameter with the function's return type. + ast::GenericParam::TypeParam(_) if !inserted_ret_type => { + inserted_ret_type = true; + make.type_arg(ret_type.clone()).into() + } + ast::GenericParam::LifetimeParam(_) => { + make.lifetime_arg(make.lifetime("'_")).into() + } + _ => make.type_arg(make.ty_infer().into()).into(), } - ast::GenericParam::LifetimeParam(_) => make::lifetime("'_").to_smolstr(), - _ => make::ty_placeholder().to_smolstr(), - }) - .join(", "); + }); let name = alias.name(ctx.db()); - let name = name.as_str(); - Some(make::ty(&format!("{name}<{generic_params}>"))) + let generic_arg_list = make.generic_arg_list(generic_args, false); + let path = make.path_unqualified( + make.path_segment_generics(make.name_ref(name.as_str()), generic_arg_list), + ); + + Some(make.ty_path(path)) }) }) } diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index d62c01ba76..af7b3c8158 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1,6 +1,9 @@ //! Wrappers over [`make`] constructors use crate::{ - ast::{self, make, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, HasVisibility}, + ast::{ + self, make, HasArgList, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, + HasVisibility, + }, syntax_editor::SyntaxMappingBuilder, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, }; @@ -16,6 +19,10 @@ impl SyntaxFactory { make::name_ref(name).clone_for_update() } + pub fn lifetime(&self, text: &str) -> ast::Lifetime { + make::lifetime(text).clone_for_update() + } + pub fn ty(&self, text: &str) -> ast::Type { make::ty(text).clone_for_update() } @@ -28,6 +35,20 @@ impl SyntaxFactory { ast } + pub fn ty_path(&self, path: ast::Path) -> ast::PathType { + let ast::Type::PathType(ast) = make::ty_path(path.clone()).clone_for_update() else { + unreachable!() + }; + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn type_param( &self, name: ast::Name, @@ -253,6 +274,37 @@ impl SyntaxFactory { ast } + pub fn expr_call(&self, expr: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr { + // FIXME: `make::expr_call`` should return a `CallExpr`, not just an `Expr` + let ast::Expr::CallExpr(ast) = + make::expr_call(expr.clone(), arg_list.clone()).clone_for_update() + else { + unreachable!() + }; + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.map_node(arg_list.syntax().clone(), ast.arg_list().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn arg_list(&self, args: impl IntoIterator) -> ast::ArgList { + let (args, input) = iterator_input(args); + let ast = make::arg_list(args).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax.clone()); + builder.map_children(input.into_iter(), ast.args().map(|it| it.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr { let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update() else { @@ -428,6 +480,30 @@ impl SyntaxFactory { ast } + pub fn type_arg(&self, ty: ast::Type) -> ast::TypeArg { + let ast = make::type_arg(ty.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn lifetime_arg(&self, lifetime: ast::Lifetime) -> ast::LifetimeArg { + let ast = make::lifetime_arg(lifetime.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(lifetime.syntax().clone(), ast.lifetime().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn item_const( &self, visibility: Option, @@ -495,12 +571,17 @@ impl SyntaxFactory { ast } - pub fn turbofish_generic_arg_list( + pub fn generic_arg_list( &self, generic_args: impl IntoIterator, + is_turbo: bool, ) -> ast::GenericArgList { let (generic_args, input) = iterator_input(generic_args); - let ast = make::turbofish_generic_arg_list(generic_args.clone()).clone_for_update(); + let ast = if is_turbo { + make::turbofish_generic_arg_list(generic_args).clone_for_update() + } else { + make::generic_arg_list(generic_args).clone_for_update() + }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -753,12 +834,31 @@ impl SyntaxFactory { // `ext` constructors impl SyntaxFactory { + pub fn ident_path(&self, ident: &str) -> ast::Path { + self.path_unqualified(self.path_segment(self.name_ref(ident))) + } + pub fn expr_unit(&self) -> ast::Expr { self.expr_tuple([]).into() } - pub fn ident_path(&self, ident: &str) -> ast::Path { - self.path_unqualified(self.path_segment(self.name_ref(ident))) + pub fn ty_option(&self, t: ast::Type) -> ast::PathType { + let generic_arg_list = self.generic_arg_list([self.type_arg(t).into()], false); + let path = self.path_unqualified( + self.path_segment_generics(self.name_ref("Option"), generic_arg_list), + ); + + self.ty_path(path) + } + + pub fn ty_result(&self, t: ast::Type, e: ast::Type) -> ast::PathType { + let generic_arg_list = + self.generic_arg_list([self.type_arg(t).into(), self.type_arg(e).into()], false); + let path = self.path_unqualified( + self.path_segment_generics(self.name_ref("Result"), generic_arg_list), + ); + + self.ty_path(path) } } From a5a79f5957f2a821ea8562c2b9d04e9304378d7e Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Sun, 17 Nov 2024 14:03:28 -0500 Subject: [PATCH 23/98] internal: Migrate `unwrap_return_type` assist to use `SyntaxEditor` Also changes `make::expr_empty_block()` to return `ast::BlockExpr` instead of `ast::Expr` --- .../src/handlers/unwrap_return_type.rs | 124 ++++++++++-------- .../src/utils/gen_trait_fn_body.rs | 2 +- crates/syntax/src/ast/make.rs | 4 +- .../src/ast/syntax_factory/constructors.rs | 2 +- 4 files changed, 75 insertions(+), 57 deletions(-) diff --git a/crates/ide-assists/src/handlers/unwrap_return_type.rs b/crates/ide-assists/src/handlers/unwrap_return_type.rs index 64d5e2c9b8..6fd46260c0 100644 --- a/crates/ide-assists/src/handlers/unwrap_return_type.rs +++ b/crates/ide-assists/src/handlers/unwrap_return_type.rs @@ -1,11 +1,11 @@ +use either::Either; use ide_db::{ famous_defs::FamousDefs, syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, }; -use itertools::Itertools; use syntax::{ - ast::{self, Expr, HasGenericArgs}, - match_ast, AstNode, NodeOrToken, SyntaxKind, TextRange, + ast::{self, syntax_factory::SyntaxFactory, HasArgList, HasGenericArgs}, + match_ast, AstNode, NodeOrToken, SyntaxKind, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -39,11 +39,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let ret_type = ctx.find_node_at_offset::()?; let parent = ret_type.syntax().parent()?; - let body = match_ast! { + let body_expr = match_ast! { match parent { - ast::Fn(func) => func.body()?, + ast::Fn(func) => func.body()?.into(), ast::ClosureExpr(closure) => match closure.body()? { - Expr::BlockExpr(block) => block, + ast::Expr::BlockExpr(block) => block.into(), // closures require a block when a return type is specified _ => return None, }, @@ -65,72 +65,94 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> let happy_type = extract_wrapped_type(type_ref)?; acc.add(kind.assist_id(), kind.label(), type_ref.syntax().text_range(), |builder| { - let body = ast::Expr::BlockExpr(body); + let mut editor = builder.make_editor(&parent); + let make = SyntaxFactory::new(); let mut exprs_to_unwrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e); - walk_expr(&body, &mut |expr| { - if let Expr::ReturnExpr(ret_expr) = expr { + walk_expr(&body_expr, &mut |expr| { + if let ast::Expr::ReturnExpr(ret_expr) = expr { if let Some(ret_expr_arg) = &ret_expr.expr() { for_each_tail_expr(ret_expr_arg, tail_cb); } } }); - for_each_tail_expr(&body, tail_cb); + for_each_tail_expr(&body_expr, tail_cb); let is_unit_type = is_unit_type(&happy_type); if is_unit_type { - let mut text_range = ret_type.syntax().text_range(); - if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() { if token.kind() == SyntaxKind::WHITESPACE { - text_range = TextRange::new(text_range.start(), token.text_range().end()); + editor.delete(token); } } - builder.delete(text_range); + editor.delete(ret_type.syntax()); } else { - builder.replace(type_ref.syntax().text_range(), happy_type.syntax().text()); + editor.replace(type_ref.syntax(), happy_type.syntax()); } - for ret_expr_arg in exprs_to_unwrap { - let ret_expr_str = ret_expr_arg.to_string(); + for tail_expr in exprs_to_unwrap { + match &tail_expr { + ast::Expr::CallExpr(call_expr) => { + let ast::Expr::PathExpr(path_expr) = call_expr.expr().unwrap() else { + continue; + }; - let needs_replacing = match kind { - UnwrapperKind::Option => ret_expr_str.starts_with("Some("), - UnwrapperKind::Result => { - ret_expr_str.starts_with("Ok(") || ret_expr_str.starts_with("Err(") - } - }; + let path_str = path_expr.path().unwrap().to_string(); + let needs_replacing = match kind { + UnwrapperKind::Option => path_str == "Some", + UnwrapperKind::Result => path_str == "Ok" || path_str == "Err", + }; - if needs_replacing { - let arg_list = ret_expr_arg.syntax().children().find_map(ast::ArgList::cast); - if let Some(arg_list) = arg_list { + if !needs_replacing { + continue; + } + + let arg_list = call_expr.arg_list().unwrap(); if is_unit_type { - match ret_expr_arg.syntax().prev_sibling_or_token() { - // Useful to delete the entire line without leaving trailing whitespaces - Some(whitespace) => { - let new_range = TextRange::new( - whitespace.text_range().start(), - ret_expr_arg.syntax().text_range().end(), - ); - builder.delete(new_range); + let tail_parent = tail_expr + .syntax() + .parent() + .and_then(Either::::cast) + .unwrap(); + match tail_parent { + Either::Left(ret_expr) => { + editor.replace(ret_expr.syntax(), make.expr_return(None).syntax()) } - None => { - builder.delete(ret_expr_arg.syntax().text_range()); + Either::Right(stmt_list) => { + let new_block = if stmt_list.statements().next().is_none() { + make.expr_empty_block() + } else { + make.block_expr(stmt_list.statements(), None) + }; + editor.replace( + stmt_list.syntax(), + new_block.stmt_list().unwrap().syntax(), + ); } } - } else { - builder.replace( - ret_expr_arg.syntax().text_range(), - arg_list.args().join(", "), - ); + } else if let Some(first_arg) = arg_list.args().next() { + editor.replace(tail_expr.syntax(), first_arg.syntax()); } } - } else if matches!(kind, UnwrapperKind::Option if ret_expr_str == "None") { - builder.replace(ret_expr_arg.syntax().text_range(), "()"); + ast::Expr::PathExpr(path_expr) => { + let UnwrapperKind::Option = kind else { + continue; + }; + + if path_expr.path().unwrap().to_string() != "None" { + continue; + } + + editor.replace(path_expr.syntax(), make.expr_unit().syntax()); + } + _ => (), } } + + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.file_id(), editor); }) } @@ -168,12 +190,12 @@ impl UnwrapperKind { fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { match e { - Expr::BreakExpr(break_expr) => { + ast::Expr::BreakExpr(break_expr) => { if let Some(break_expr_arg) = break_expr.expr() { for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e)) } } - Expr::ReturnExpr(_) => { + ast::Expr::ReturnExpr(_) => { // all return expressions have already been handled by the walk loop } e => acc.push(e.clone()), @@ -238,8 +260,7 @@ fn foo() -> Option<()$0> { } "#, r#" -fn foo() { -} +fn foo() {} "#, "Unwrap Option return type", ); @@ -254,8 +275,7 @@ fn foo() -> Option<()$0>{ } "#, r#" -fn foo() { -} +fn foo() {} "#, "Unwrap Option return type", ); @@ -1262,8 +1282,7 @@ fn foo() -> Result<(), Box> { } "#, r#" -fn foo() { -} +fn foo() {} "#, "Unwrap Result return type", ); @@ -1278,8 +1297,7 @@ fn foo() -> Result<(), Box>{ } "#, r#" -fn foo() { -} +fn foo() {} "#, "Unwrap Result return type", ); diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index a9a889acc2..7a9bdfe1ec 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -599,7 +599,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) let variant_name = make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?); let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]); - arms.push(make::match_arm(lhs.into(), None, make::expr_empty_block())); + arms.push(make::match_arm(lhs.into(), None, make::expr_empty_block().into())); arms.push(make::match_arm( make::ident_pat(false, false, make::name("ord")).into(), diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index a920fe4f35..dca231604f 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -558,8 +558,8 @@ pub fn expr_const_value(text: &str) -> ast::ConstArg { ast_from_text(&format!("trait Foo {{}}")) } -pub fn expr_empty_block() -> ast::Expr { - expr_from_text("{}") +pub fn expr_empty_block() -> ast::BlockExpr { + ast_from_text("const C: () = {};") } pub fn expr_path(path: ast::Path) -> ast::Expr { expr_from_text(&path.to_string()) diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index af7b3c8158..572622db54 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -211,7 +211,7 @@ impl SyntaxFactory { } pub fn expr_empty_block(&self) -> ast::BlockExpr { - ast::BlockExpr { syntax: make::expr_empty_block().syntax().clone_for_update() } + make::expr_empty_block().clone_for_update() } pub fn expr_tuple(&self, fields: impl IntoIterator) -> ast::TupleExpr { From c552f72f6a990b1048e4b7537070f02e420435d4 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Sun, 17 Nov 2024 14:34:05 -0500 Subject: [PATCH 24/98] minor: Use placeholders in `unwrap_return_type` --- .../src/handlers/unwrap_return_type.rs | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/crates/ide-assists/src/handlers/unwrap_return_type.rs b/crates/ide-assists/src/handlers/unwrap_return_type.rs index 6fd46260c0..f647b531b7 100644 --- a/crates/ide-assists/src/handlers/unwrap_return_type.rs +++ b/crates/ide-assists/src/handlers/unwrap_return_type.rs @@ -92,6 +92,7 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> editor.replace(type_ref.syntax(), happy_type.syntax()); } + let mut final_placeholder = None; for tail_expr in exprs_to_unwrap { match &tail_expr { ast::Expr::CallExpr(call_expr) => { @@ -145,12 +146,27 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> continue; } - editor.replace(path_expr.syntax(), make.expr_unit().syntax()); + let new_tail_expr = make.expr_unit(); + editor.replace(path_expr.syntax(), new_tail_expr.syntax()); + if let Some(cap) = ctx.config.snippet_cap { + editor.add_annotation( + new_tail_expr.syntax(), + builder.make_placeholder_snippet(cap), + ); + + final_placeholder = Some(new_tail_expr); + } } _ => (), } } + if let Some(cap) = ctx.config.snippet_cap { + if let Some(final_placeholder) = final_placeholder { + editor.add_annotation(final_placeholder.syntax(), builder.make_tabstop_after(cap)); + } + } + editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.file_id(), editor); }) @@ -300,7 +316,42 @@ fn foo() -> i32 { if true { 42 } else { - () + ${1:()}$0 + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_multi_none() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + if false { + return None; + } + + if true { + Some(42) + } else { + None + } +} +"#, + r#" +fn foo() -> i32 { + if false { + return ${1:()}; + } + + if true { + 42 + } else { + ${2:()}$0 } } "#, From 0c77bce011bf219f4574f6dfcdd102b270d96f2f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 9 Jan 2025 05:22:14 +0200 Subject: [PATCH 25/98] Fix actual token lookup in completion's `expand()` It should be left biased, not right biased, because when e.g. the use has typed `h` then requested completion, the `h` is what we want to find, not the next token (which might indeed be inside a macro call). I'm not sure why I wrote `right_biased()` to begin with (I remember I had a reason and not just "both should work"), I might've copied the code in `expand_and_analyze()` (which is wrong, because there it lookups on the speculative file, where right biased will always find the correct token and left biased not). This is still not perfect, because there might not be an identifier already typed then we might still end up in a macro call, but this is the best we can do. --- crates/ide-completion/src/context/analysis.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index acce62a041..1a5609baf9 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -123,10 +123,11 @@ fn expand( ) -> Option { let _p = tracing::info_span!("CompletionContext::expand").entered(); + // Left biased since there may already be an identifier token there, and we appended to it. if !sema.might_be_inside_macro_call(&fake_ident_token) && original_file .token_at_offset(original_offset + relative_offset) - .right_biased() + .left_biased() .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token)) { // Recursion base case. From 97afb7bfba885304fe03d14984b39a3ba842578a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 7 Jan 2025 08:00:18 +0200 Subject: [PATCH 26/98] Make edition per-token, not per-file More correctly, *also* per-token. Because as it turns out, while the top-level edition affects parsing (I think), the per-token edition affects escaping of identifiers/keywords. --- crates/edition/src/lib.rs | 3 +- crates/hir-def/src/body.rs | 15 +- crates/hir-def/src/body/lower.rs | 2 +- crates/hir-def/src/expander.rs | 4 +- crates/hir-def/src/item_tree/tests.rs | 2 +- .../hir-def/src/macro_expansion_tests/mbe.rs | 30 +-- .../hir-def/src/macro_expansion_tests/mod.rs | 1 + .../src/macro_expansion_tests/proc_macros.rs | 6 +- crates/hir-def/src/nameres/collector.rs | 2 +- crates/hir-def/src/resolver.rs | 2 +- crates/hir-expand/src/builtin/derive_macro.rs | 105 ++++++++--- crates/hir-expand/src/builtin/fn_macro.rs | 10 +- crates/hir-expand/src/builtin/quote.rs | 8 +- crates/hir-expand/src/db.rs | 37 ++-- crates/hir-expand/src/declarative.rs | 11 +- crates/hir-expand/src/files.rs | 11 +- crates/hir-expand/src/fixup.rs | 3 +- crates/hir-expand/src/hygiene.rs | 46 +++-- crates/hir-expand/src/mod_path.rs | 7 +- crates/hir-expand/src/name.rs | 11 +- crates/hir/src/attrs.rs | 5 +- crates/hir/src/lib.rs | 4 +- crates/hir/src/semantics.rs | 7 +- .../src/handlers/no_such_field.rs | 34 ++++ crates/mbe/src/lib.rs | 3 +- crates/mbe/src/tests.rs | 177 +++++++++--------- .../proc-macro-api/src/legacy_protocol/msg.rs | 20 +- .../src/server_impl/rust_analyzer_span.rs | 4 +- crates/proc-macro-srv/src/tests/mod.rs | 130 ++++++------- crates/proc-macro-srv/src/tests/utils.rs | 4 +- crates/span/src/hygiene.rs | 29 ++- crates/span/src/map.rs | 2 +- crates/syntax-bridge/src/lib.rs | 17 +- crates/syntax-bridge/src/to_parser_input.rs | 44 +++-- 34 files changed, 480 insertions(+), 316 deletions(-) diff --git a/crates/edition/src/lib.rs b/crates/edition/src/lib.rs index c25d5b9557..7e9c94af40 100644 --- a/crates/edition/src/lib.rs +++ b/crates/edition/src/lib.rs @@ -5,7 +5,8 @@ use std::fmt; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum Edition { - Edition2015, + // The syntax context stuff needs the discriminants to start from 0 and be consecutive. + Edition2015 = 0, Edition2018, Edition2021, Edition2024, diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 433a956ff9..de43924930 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -15,7 +15,7 @@ use hir_expand::{name::Name, ExpandError, InFile}; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use rustc_hash::FxHashMap; use smallvec::SmallVec; -use span::{Edition, MacroFileId}; +use span::{Edition, MacroFileId, SyntaxContextData}; use syntax::{ast, AstPtr, SyntaxNodePtr}; use triomphe::Arc; use tt::TextRange; @@ -37,15 +37,22 @@ use crate::{ /// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct HygieneId(pub(crate) span::SyntaxContextId); +pub struct HygieneId(span::SyntaxContextId); impl HygieneId { - pub const ROOT: Self = Self(span::SyntaxContextId::ROOT); + // The edition doesn't matter here, we only use this for comparisons and to lookup the macro. + pub const ROOT: Self = Self(span::SyntaxContextId::root(Edition::Edition2015)); - pub fn new(ctx: span::SyntaxContextId) -> Self { + pub fn new(mut ctx: span::SyntaxContextId) -> Self { + // See `Name` for why we're doing that. + ctx.remove_root_edition(); Self(ctx) } + pub(crate) fn lookup(self, db: &dyn DefDatabase) -> SyntaxContextData { + db.lookup_intern_syntax_context(self.0) + } + pub(crate) fn is_root(self) -> bool { self.0.is_root() } diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index eed9f9468f..71d8b81236 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -2460,7 +2460,7 @@ impl ExprCollector<'_> { None => HygieneId::ROOT, Some(span_map) => { let ctx = span_map.span_at(span_start).ctx; - HygieneId(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent) + HygieneId::new(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent) } } } diff --git a/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs index 5315c1c6fb..108258d5a1 100644 --- a/crates/hir-def/src/expander.rs +++ b/crates/hir-def/src/expander.rs @@ -10,7 +10,7 @@ use hir_expand::{ ExpandResult, HirFileId, InFile, Lookup, MacroCallId, }; use limit::Limit; -use span::SyntaxContextId; +use span::{Edition, SyntaxContextId}; use syntax::{ast, Parse}; use triomphe::Arc; @@ -60,7 +60,7 @@ impl Expander { pub fn syntax_context(&self) -> SyntaxContextId { // FIXME: - SyntaxContextId::ROOT + SyntaxContextId::root(Edition::CURRENT) } pub fn enter_expand( diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 0f53969d6c..735586c049 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -270,7 +270,7 @@ m!(); // AstId: 2 pub macro m2 { ... } - // AstId: 3, SyntaxContext: 0, ExpandTo: Items + // AstId: 3, SyntaxContext: 2, ExpandTo: Items m!(...); "#]], ); diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index 511626b5ed..8c5bd3b6d3 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -35,9 +35,9 @@ macro_rules! f { }; } -struct#0:1@58..64#1# MyTraitMap2#0:2@31..42#0# {#0:1@72..73#1# - map#0:1@86..89#1#:#0:1@89..90#1# #0:1@89..90#1#::#0:1@91..93#1#std#0:1@93..96#1#::#0:1@96..98#1#collections#0:1@98..109#1#::#0:1@109..111#1#HashSet#0:1@111..118#1#<#0:1@118..119#1#(#0:1@119..120#1#)#0:1@120..121#1#>#0:1@121..122#1#,#0:1@122..123#1# -}#0:1@132..133#1# +struct#0:1@58..64#4# MyTraitMap2#0:2@31..42#2# {#0:1@72..73#4# + map#0:1@86..89#4#:#0:1@89..90#4# #0:1@89..90#4#::#0:1@91..93#4#std#0:1@93..96#4#::#0:1@96..98#4#collections#0:1@98..109#4#::#0:1@109..111#4#HashSet#0:1@111..118#4#<#0:1@118..119#4#(#0:1@119..120#4#)#0:1@120..121#4#>#0:1@121..122#4#,#0:1@122..123#4# +}#0:1@132..133#4# "#]], ); } @@ -75,12 +75,12 @@ macro_rules! f { }; } -fn#0:2@30..32#0# main#0:2@33..37#0#(#0:2@37..38#0#)#0:2@38..39#0# {#0:2@40..41#0# - 1#0:2@50..51#0#;#0:2@51..52#0# - 1.0#0:2@61..64#0#;#0:2@64..65#0# - (#0:2@74..75#0#(#0:2@75..76#0#1#0:2@76..77#0#,#0:2@77..78#0# )#0:2@78..79#0#,#0:2@79..80#0# )#0:2@80..81#0#.#0:2@81..82#0#0#0:2@82..85#0#.#0:2@82..85#0#0#0:2@82..85#0#;#0:2@85..86#0# - let#0:2@95..98#0# x#0:2@99..100#0# =#0:2@101..102#0# 1#0:2@103..104#0#;#0:2@104..105#0# -}#0:2@110..111#0# +fn#0:2@30..32#2# main#0:2@33..37#2#(#0:2@37..38#2#)#0:2@38..39#2# {#0:2@40..41#2# + 1#0:2@50..51#2#;#0:2@51..52#2# + 1.0#0:2@61..64#2#;#0:2@64..65#2# + (#0:2@74..75#2#(#0:2@75..76#2#1#0:2@76..77#2#,#0:2@77..78#2# )#0:2@78..79#2#,#0:2@79..80#2# )#0:2@80..81#2#.#0:2@81..82#2#0#0:2@82..85#2#.#0:2@82..85#2#0#0:2@82..85#2#;#0:2@85..86#2# + let#0:2@95..98#2# x#0:2@99..100#2# =#0:2@101..102#2# 1#0:2@103..104#2#;#0:2@104..105#2# +}#0:2@110..111#2# "#]], @@ -171,7 +171,7 @@ fn main(foo: ()) { } fn main(foo: ()) { - /* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#0#; + /* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#2#; } } @@ -197,7 +197,7 @@ macro_rules! mk_struct { #[macro_use] mod foo; -struct#1:1@59..65#1# Foo#0:2@32..35#0#(#1:1@70..71#1#u32#0:2@41..44#0#)#1:1@74..75#1#;#1:1@75..76#1# +struct#1:1@59..65#4# Foo#0:2@32..35#2#(#1:1@70..71#4#u32#0:2@41..44#2#)#1:1@74..75#4#;#1:1@75..76#4# "#]], ); } @@ -423,10 +423,10 @@ m! { foo, bar } macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } -impl#\1# Bar#\1# {#\1# - fn#\1# foo#\0#(#\1#)#\1# {#\1#}#\1# - fn#\1# bar#\0#(#\1#)#\1# {#\1#}#\1# -}#\1# +impl#\4# Bar#\4# {#\4# + fn#\4# foo#\2#(#\4#)#\4# {#\4#}#\4# + fn#\4# bar#\2#(#\4#)#\4# {#\4#}#\4# +}#\4# "#]], ); } diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index 5b9ffdf37b..adcf278472 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -358,6 +358,7 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { let (parse, _) = syntax_bridge::token_tree_to_syntax_node( subtree, syntax_bridge::TopEntryPoint::MacroItems, + &mut |_| span::Edition::CURRENT, span::Edition::CURRENT, ); if parse.errors().is_empty() { diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index c0178adc9a..70e3e1ed4e 100644 --- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -181,9 +181,9 @@ fn foo(&self) { self.0. 1; } -fn#0:1@45..47#0# foo#0:1@48..51#0#(#0:1@51..52#0#�:1@52..53#0#self#0:1@53..57#0# )#0:1@57..58#0# {#0:1@59..60#0# - self#0:1@65..69#0# .#0:1@69..70#0#0#0:1@70..71#0#.#0:1@71..72#0#1#0:1@73..74#0#;#0:1@74..75#0# -}#0:1@76..77#0#"#]], +fn#0:1@45..47#2# foo#0:1@48..51#2#(#0:1@51..52#2#�:1@52..53#2#self#0:1@53..57#2# )#0:1@57..58#2# {#0:1@59..60#2# + self#0:1@65..69#2# .#0:1@69..70#2#0#0:1@70..71#2#.#0:1@71..72#2#1#0:1@73..74#2#;#0:1@74..75#2# +}#0:1@76..77#2#"#]], ); } diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index de76257587..988e5e83c8 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -74,7 +74,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI let proc_macros = if krate.is_proc_macro { db.proc_macros() - .for_crate(def_map.krate, db.syntax_context(tree_id.file_id())) + .for_crate(def_map.krate, db.syntax_context(tree_id.file_id(), krate.edition)) .unwrap_or_default() } else { Default::default() diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 82da57a9bb..df48ce6737 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -324,7 +324,7 @@ impl Resolver { if n_segments <= 1 { let mut hygiene_info = if !hygiene_id.is_root() { - let ctx = db.lookup_intern_syntax_context(hygiene_id.0); + let ctx = hygiene_id.lookup(db); ctx.outer_expn.map(|expansion| { let expansion = db.lookup_intern_macro_call(expansion); (ctx.parent, expansion.def) diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs index 4510a593af..28b6812139 100644 --- a/crates/hir-expand/src/builtin/derive_macro.rs +++ b/crates/hir-expand/src/builtin/derive_macro.rs @@ -4,7 +4,7 @@ use intern::sym; use itertools::{izip, Itertools}; use parser::SyntaxKind; use rustc_hash::FxHashSet; -use span::{MacroCallId, Span, SyntaxContextId}; +use span::{Edition, MacroCallId, Span, SyntaxContextId}; use stdx::never; use syntax_bridge::DocCommentDesugarMode; use tracing::debug; @@ -33,7 +33,7 @@ macro_rules! register_builtin { } impl BuiltinDeriveExpander { - pub fn expander(&self) -> fn(Span, &tt::TopSubtree) -> ExpandResult { + pub fn expander(&self) -> fn(&dyn ExpandDatabase, Span, &tt::TopSubtree) -> ExpandResult { match *self { $( BuiltinDeriveExpander::$trait => $expand, )* } @@ -58,8 +58,8 @@ impl BuiltinDeriveExpander { tt: &tt::TopSubtree, span: Span, ) -> ExpandResult { - let span = span_with_def_site_ctxt(db, span, id); - self.expander()(span, tt) + let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT); + self.expander()(db, span, tt) } } @@ -226,8 +226,12 @@ struct AdtParam { } // FIXME: This whole thing needs a refactor. Each derive requires its special values, and the result is a mess. -fn parse_adt(tt: &tt::TopSubtree, call_site: Span) -> Result { - let (adt, tm) = to_adt_syntax(tt, call_site)?; +fn parse_adt( + db: &dyn ExpandDatabase, + tt: &tt::TopSubtree, + call_site: Span, +) -> Result { + let (adt, tm) = to_adt_syntax(db, tt, call_site)?; parse_adt_from_syntax(&adt, &tm, call_site) } @@ -382,12 +386,14 @@ fn parse_adt_from_syntax( } fn to_adt_syntax( + db: &dyn ExpandDatabase, tt: &tt::TopSubtree, call_site: Span, ) -> Result<(ast::Adt, span::SpanMap), ExpandError> { - let (parsed, tm) = syntax_bridge::token_tree_to_syntax_node( + let (parsed, tm) = crate::db::token_tree_to_syntax_node( + db, tt, - syntax_bridge::TopEntryPoint::MacroItems, + crate::ExpandTo::Items, parser::Edition::CURRENT_FIXME, ); let macro_items = ast::MacroItems::cast(parsed.syntax_node()) @@ -446,12 +452,13 @@ fn name_to_token( /// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and /// therefore does not get bound by the derived trait. fn expand_simple_derive( + db: &dyn ExpandDatabase, invoc_span: Span, tt: &tt::TopSubtree, trait_path: tt::TopSubtree, make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree, ) -> ExpandResult { - let info = match parse_adt(tt, invoc_span) { + let info = match parse_adt(db, tt, invoc_span) { Ok(info) => info, Err(e) => { return ExpandResult::new( @@ -520,14 +527,22 @@ fn expand_simple_derive_with_parsed( } } -fn copy_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { +fn copy_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { let krate = dollar_crate(span); - expand_simple_derive(span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>}) + expand_simple_derive(db, span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>}) } -fn clone_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { +fn clone_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { let krate = dollar_crate(span); - expand_simple_derive(span, tt, quote! {span => #krate::clone::Clone }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::clone::Clone }, |adt| { if matches!(adt.shape, AdtShape::Union) { let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span }; return quote! {span => @@ -576,9 +591,13 @@ fn and_and(span: Span) -> tt::TopSubtree { quote! {span => #and& } } -fn default_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { +fn default_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(span, tt, quote! {span => #krate::default::Default }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::default::Default }, |adt| { let body = match &adt.shape { AdtShape::Struct(fields) => { let name = &adt.name; @@ -615,9 +634,13 @@ fn default_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult ExpandResult { +fn debug_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(span, tt, quote! {span => #krate::fmt::Debug }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, |adt| { let for_variant = |name: String, v: &VariantShape| match v { VariantShape::Struct(fields) => { let for_fields = fields.iter().map(|it| { @@ -687,9 +710,13 @@ fn debug_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult }) } -fn hash_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { +fn hash_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(span, tt, quote! {span => #krate::hash::Hash }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::hash::Hash }, |adt| { if matches!(adt.shape, AdtShape::Union) { // FIXME: Return expand error here return quote! {span =>}; @@ -734,14 +761,22 @@ fn hash_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult }) } -fn eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { +fn eq_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { let krate = dollar_crate(span); - expand_simple_derive(span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>}) + expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>}) } -fn partial_eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { +fn partial_eq_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { let krate = dollar_crate(span); - expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| { if matches!(adt.shape, AdtShape::Union) { // FIXME: Return expand error here return quote! {span =>}; @@ -811,9 +846,13 @@ fn self_and_other_patterns( (self_patterns, other_patterns) } -fn ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { +fn ord_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(span, tt, quote! {span => #krate::cmp::Ord }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Ord }, |adt| { fn compare( krate: &tt::Ident, left: tt::TopSubtree, @@ -869,9 +908,13 @@ fn ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { }) } -fn partial_ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { +fn partial_ord_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| { fn compare( krate: &tt::Ident, left: tt::TopSubtree, @@ -932,8 +975,12 @@ fn partial_ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult ExpandResult { - let (adt, _span_map) = match to_adt_syntax(tt, span) { +fn coerce_pointee_expand( + db: &dyn ExpandDatabase, + span: Span, + tt: &tt::TopSubtree, +) -> ExpandResult { + let (adt, _span_map) = match to_adt_syntax(db, tt, span) { Ok(it) => it, Err(err) => { return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err); diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs index 5b06de9875..310ddaaf9e 100644 --- a/crates/hir-expand/src/builtin/fn_macro.rs +++ b/crates/hir-expand/src/builtin/fn_macro.rs @@ -69,7 +69,7 @@ impl BuiltinFnLikeExpander { tt: &tt::TopSubtree, span: Span, ) -> ExpandResult { - let span = span_with_def_site_ctxt(db, span, id); + let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT); self.expander()(db, id, tt, span) } @@ -86,7 +86,7 @@ impl EagerExpander { tt: &tt::TopSubtree, span: Span, ) -> ExpandResult { - let span = span_with_def_site_ctxt(db, span, id); + let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT); self.expander()(db, id, tt, span) } @@ -221,7 +221,7 @@ fn assert_expand( tt: &tt::TopSubtree, span: Span, ) -> ExpandResult { - let call_site_span = span_with_call_site_ctxt(db, span, id); + let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT); let mut iter = tt.iter(); @@ -342,7 +342,7 @@ fn panic_expand( span: Span, ) -> ExpandResult { let dollar_crate = dollar_crate(span); - let call_site_span = span_with_call_site_ctxt(db, span, id); + let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT); let mac = if use_panic_2021(db, call_site_span) { sym::panic_2021.clone() @@ -373,7 +373,7 @@ fn unreachable_expand( span: Span, ) -> ExpandResult { let dollar_crate = dollar_crate(span); - let call_site_span = span_with_call_site_ctxt(db, span, id); + let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT); let mac = if use_panic_2021(db, call_site_span) { sym::unreachable_2021.clone() diff --git a/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs index 6c1abc2620..fcd242bb49 100644 --- a/crates/hir-expand/src/builtin/quote.rs +++ b/crates/hir-expand/src/builtin/quote.rs @@ -225,7 +225,7 @@ mod tests { use ::tt::IdentIsRaw; use expect_test::expect; use intern::Symbol; - use span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; + use span::{Edition, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use syntax::{TextRange, TextSize}; use super::quote; @@ -239,7 +239,7 @@ mod tests { ), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }; #[test] @@ -276,8 +276,8 @@ mod tests { assert_eq!(quoted.to_string(), "hello"); let t = format!("{quoted:#?}"); expect![[r#" - SUBTREE $$ 937550:0@0..0#0 937550:0@0..0#0 - IDENT hello 937550:0@0..0#0"#]] + SUBTREE $$ 937550:0@0..0#2 937550:0@0..0#2 + IDENT hello 937550:0@0..0#2"#]] .assert_eq(&t); } diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index f4e80ef9e2..b7804f888a 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -5,7 +5,7 @@ use either::Either; use limit::Limit; use mbe::MatchedArmIndex; use rustc_hash::FxHashSet; -use span::{AstIdMap, EditionedFileId, Span, SyntaxContextData, SyntaxContextId}; +use span::{AstIdMap, Edition, EditionedFileId, Span, SyntaxContextData, SyntaxContextId}; use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T}; use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode}; use triomphe::Arc; @@ -136,12 +136,12 @@ pub trait ExpandDatabase: SourceDatabase { macro_call: MacroCallId, ) -> Option>>>; #[ra_salsa::transparent] - fn syntax_context(&self, file: HirFileId) -> SyntaxContextId; + fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContextId; } -fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId) -> SyntaxContextId { +fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> SyntaxContextId { match file.repr() { - HirFileIdRepr::FileId(_) => SyntaxContextId::ROOT, + HirFileIdRepr::FileId(_) => SyntaxContextId::root(edition), HirFileIdRepr::MacroFile(m) => { db.macro_arg_considering_derives(m.macro_call_id, &m.macro_call_id.lookup(db).kind) .2 @@ -273,9 +273,9 @@ pub fn expand_speculative( loc.krate, &tt, attr_arg.as_ref(), - span_with_def_site_ctxt(db, span, actual_macro_call), - span_with_call_site_ctxt(db, span, actual_macro_call), - span_with_mixed_site_ctxt(db, span, actual_macro_call), + span_with_def_site_ctxt(db, span, actual_macro_call, loc.def.edition), + span_with_call_site_ctxt(db, span, actual_macro_call, loc.def.edition), + span_with_mixed_site_ctxt(db, span, actual_macro_call, loc.def.edition), ) } MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => { @@ -300,7 +300,7 @@ pub fn expand_speculative( fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info); let (node, rev_tmap) = - token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition); + token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to, loc.def.edition); let syntax_node = node.syntax_node(); let token = rev_tmap @@ -346,6 +346,7 @@ fn parse_macro_expansion( macro_expand(db, macro_file.macro_call_id, loc); let (parse, mut rev_token_map) = token_tree_to_syntax_node( + db, match &tt { CowArc::Arc(it) => it, CowArc::Owned(it) => it, @@ -699,9 +700,9 @@ fn expand_proc_macro( loc.krate, ¯o_arg, attr_arg, - span_with_def_site_ctxt(db, span, id), - span_with_call_site_ctxt(db, span, id), - span_with_mixed_site_ctxt(db, span, id), + span_with_def_site_ctxt(db, span, id, loc.def.edition), + span_with_call_site_ctxt(db, span, id, loc.def.edition), + span_with_mixed_site_ctxt(db, span, id, loc.def.edition), ) }; @@ -715,7 +716,8 @@ fn expand_proc_macro( ExpandResult { value: Arc::new(tt), err } } -fn token_tree_to_syntax_node( +pub(crate) fn token_tree_to_syntax_node( + db: &dyn ExpandDatabase, tt: &tt::TopSubtree, expand_to: ExpandTo, edition: parser::Edition, @@ -727,7 +729,12 @@ fn token_tree_to_syntax_node( ExpandTo::Type => syntax_bridge::TopEntryPoint::Type, ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr, }; - syntax_bridge::token_tree_to_syntax_node(tt, entry_point, edition) + syntax_bridge::token_tree_to_syntax_node( + tt, + entry_point, + &mut |ctx| ctx.lookup(db).edition, + edition, + ) } fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> { @@ -751,5 +758,7 @@ fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> { } fn setup_syntax_context_root(db: &dyn ExpandDatabase) { - db.intern_syntax_context(SyntaxContextData::root()); + for edition in Edition::iter() { + db.intern_syntax_context(SyntaxContextData::root(edition)); + } } diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs index d1c39f32ca..fef77acb7b 100644 --- a/crates/hir-expand/src/declarative.rs +++ b/crates/hir-expand/src/declarative.rs @@ -2,7 +2,7 @@ use base_db::CrateId; use intern::sym; -use span::{Edition, MacroCallId, Span, SyntaxContextId}; +use span::{Edition, HirFileIdRepr, MacroCallId, Span, SyntaxContextId}; use stdx::TupleExt; use syntax::{ast, AstNode}; use syntax_bridge::DocCommentDesugarMode; @@ -20,6 +20,7 @@ use crate::{ pub struct DeclarativeMacroExpander { pub mac: mbe::DeclarativeMacro, pub transparency: Transparency, + edition: Edition, } impl DeclarativeMacroExpander { @@ -40,7 +41,7 @@ impl DeclarativeMacroExpander { .mac .expand( &tt, - |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency), + |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency, self.edition), span, loc.def.edition, ) @@ -159,6 +160,10 @@ impl DeclarativeMacroExpander { transparency(¯o_def).unwrap_or(Transparency::Opaque), ), }; - Arc::new(DeclarativeMacroExpander { mac, transparency }) + let edition = ctx_edition(match id.file_id.repr() { + HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id.lookup(db).ctxt, + HirFileIdRepr::FileId(file) => SyntaxContextId::root(file.edition()), + }); + Arc::new(DeclarativeMacroExpander { mac, transparency, edition }) } } diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs index 8c04d05402..13ddb0d4ac 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -380,14 +380,14 @@ impl InFile { ) -> (FileRange, SyntaxContextId) { match self.file_id.repr() { HirFileIdRepr::FileId(file_id) => { - (FileRange { file_id, range: self.value }, SyntaxContextId::ROOT) + (FileRange { file_id, range: self.value }, SyntaxContextId::root(file_id.edition())) } HirFileIdRepr::MacroFile(mac_file) => { match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) { Some(it) => it, None => { let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); - (loc.kind.original_call_range(db), SyntaxContextId::ROOT) + (loc.kind.original_call_range(db), SyntaxContextId::root(loc.def.edition)) } } } @@ -432,9 +432,10 @@ impl InFile { db: &dyn db::ExpandDatabase, ) -> Option<(FileRange, SyntaxContextId)> { match self.file_id.repr() { - HirFileIdRepr::FileId(file_id) => { - Some((FileRange { file_id, range: self.value }, SyntaxContextId::ROOT)) - } + HirFileIdRepr::FileId(file_id) => Some(( + FileRange { file_id, range: self.value }, + SyntaxContextId::root(file_id.edition()), + )), HirFileIdRepr::MacroFile(mac_file) => { map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) } diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 90012dd1f7..00fad67578 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -380,7 +380,7 @@ pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInf let span = |file_id| Span { range: TextRange::empty(TextSize::new(0)), anchor: SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID }, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(span::Edition::Edition2015), }; delimiter.open = span(delimiter.open.anchor.file_id); delimiter.close = span(delimiter.close.anchor.file_id); @@ -554,6 +554,7 @@ mod tests { let (parse, _) = syntax_bridge::token_tree_to_syntax_node( &tt, syntax_bridge::TopEntryPoint::MacroItems, + &mut |_| parser::Edition::CURRENT, parser::Edition::CURRENT, ); assert!( diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs index f48de807c2..fe05af0ac9 100644 --- a/crates/hir-expand/src/hygiene.rs +++ b/crates/hir-expand/src/hygiene.rs @@ -24,26 +24,37 @@ use std::iter; -use span::{MacroCallId, Span, SyntaxContextData, SyntaxContextId}; +use span::{Edition, MacroCallId, Span, SyntaxContextData, SyntaxContextId}; use crate::db::{ExpandDatabase, InternSyntaxContextQuery}; pub use span::Transparency; -pub fn span_with_def_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span { - span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque) +pub fn span_with_def_site_ctxt( + db: &dyn ExpandDatabase, + span: Span, + expn_id: MacroCallId, + edition: Edition, +) -> Span { + span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque, edition) } -pub fn span_with_call_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span { - span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent) +pub fn span_with_call_site_ctxt( + db: &dyn ExpandDatabase, + span: Span, + expn_id: MacroCallId, + edition: Edition, +) -> Span { + span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent, edition) } pub fn span_with_mixed_site_ctxt( db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId, + edition: Edition, ) -> Span { - span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent) + span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent, edition) } fn span_with_ctxt_from_mark( @@ -51,8 +62,12 @@ fn span_with_ctxt_from_mark( span: Span, expn_id: MacroCallId, transparency: Transparency, + edition: Edition, ) -> Span { - Span { ctx: apply_mark(db, SyntaxContextId::ROOT, expn_id, transparency), ..span } + Span { + ctx: apply_mark(db, SyntaxContextId::root(edition), expn_id, transparency, edition), + ..span + } } pub(super) fn apply_mark( @@ -60,9 +75,10 @@ pub(super) fn apply_mark( ctxt: SyntaxContextId, call_id: MacroCallId, transparency: Transparency, + edition: Edition, ) -> SyntaxContextId { if transparency == Transparency::Opaque { - return apply_mark_internal(db, ctxt, call_id, transparency); + return apply_mark_internal(db, ctxt, call_id, transparency, edition); } let call_site_ctxt = db.lookup_intern_macro_call(call_id).ctxt; @@ -73,7 +89,7 @@ pub(super) fn apply_mark( }; if call_site_ctxt.is_root() { - return apply_mark_internal(db, ctxt, call_id, transparency); + return apply_mark_internal(db, ctxt, call_id, transparency, edition); } // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a @@ -86,9 +102,9 @@ pub(super) fn apply_mark( // // See the example at `test/ui/hygiene/legacy_interaction.rs`. for (call_id, transparency) in ctxt.marks(db) { - call_site_ctxt = apply_mark_internal(db, call_site_ctxt, call_id, transparency); + call_site_ctxt = apply_mark_internal(db, call_site_ctxt, call_id, transparency, edition); } - apply_mark_internal(db, call_site_ctxt, call_id, transparency) + apply_mark_internal(db, call_site_ctxt, call_id, transparency, edition) } fn apply_mark_internal( @@ -96,6 +112,7 @@ fn apply_mark_internal( ctxt: SyntaxContextId, call_id: MacroCallId, transparency: Transparency, + edition: Edition, ) -> SyntaxContextId { use base_db::ra_salsa; @@ -108,13 +125,14 @@ fn apply_mark_internal( if transparency >= Transparency::Opaque { let parent = opaque; opaque = ra_salsa::plumbing::get_query_table::(db).get_or_insert( - (parent, call_id, transparency), + (parent, call_id, transparency, edition), |new_opaque| SyntaxContextData { outer_expn: call_id, outer_transparency: transparency, parent, opaque: new_opaque, opaque_and_semitransparent: new_opaque, + edition, }, ); } @@ -123,13 +141,14 @@ fn apply_mark_internal( let parent = opaque_and_semitransparent; opaque_and_semitransparent = ra_salsa::plumbing::get_query_table::(db).get_or_insert( - (parent, call_id, transparency), + (parent, call_id, transparency, edition), |new_opaque_and_semitransparent| SyntaxContextData { outer_expn: call_id, outer_transparency: transparency, parent, opaque, opaque_and_semitransparent: new_opaque_and_semitransparent, + edition, }, ); } @@ -141,6 +160,7 @@ fn apply_mark_internal( parent, opaque, opaque_and_semitransparent, + edition, }) } diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 7ecf521987..876919f3ed 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -273,10 +273,9 @@ fn convert_path( res } } - ast::PathSegmentKind::SelfTypeKw => ModPath::from_segments( - PathKind::Plain, - Some(Name::new_symbol(sym::Self_.clone(), SyntaxContextId::ROOT)), - ), + ast::PathSegmentKind::SelfTypeKw => { + ModPath::from_segments(PathKind::Plain, Some(Name::new_symbol_root(sym::Self_.clone()))) + } ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()), ast::PathSegmentKind::SelfKw => handle_super_kw(0)?, ast::PathSegmentKind::SuperKw => handle_super_kw(1)?, diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 267d545833..54a61a096d 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -80,11 +80,20 @@ impl Name { Name { symbol: Symbol::intern(text), ctx: () } } - pub fn new(text: &str, ctx: SyntaxContextId) -> Name { + pub fn new(text: &str, mut ctx: SyntaxContextId) -> Name { + // For comparisons etc. we remove the edition, because sometimes we search for some `Name` + // and we don't know which edition it came from. + // Can't do that for all `SyntaxContextId`s because it breaks Salsa. + ctx.remove_root_edition(); _ = ctx; Self::new_text(text) } + pub fn new_root(text: &str) -> Name { + // The edition doesn't matter for hygiene. + Self::new(text, SyntaxContextId::root(Edition::Edition2015)) + } + pub fn new_tuple_field(idx: usize) -> Name { Name { symbol: Symbol::intern(&idx.to_string()), ctx: () } } diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index a23fdf1b39..01cb040bdf 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -12,7 +12,6 @@ use hir_def::{ }; use hir_expand::{mod_path::PathKind, name::Name}; use hir_ty::{db::HirDatabase, method_resolution}; -use span::SyntaxContextId; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -328,9 +327,7 @@ fn doc_modpath_from_str(link: &str) -> Option { }; let parts = first_segment.into_iter().chain(parts).map(|segment| match segment.parse() { Ok(idx) => Name::new_tuple_field(idx), - Err(_) => { - Name::new(segment.split_once('<').map_or(segment, |it| it.0), SyntaxContextId::ROOT) - } + Err(_) => Name::new_root(segment.split_once('<').map_or(segment, |it| it.0)), }); Some(ModPath::from_segments(kind, parts)) }; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index fe4b255d3c..ac109be5ef 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -83,7 +83,7 @@ use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; use smallvec::SmallVec; -use span::{Edition, EditionedFileId, FileId, MacroCallId, SyntaxContextId}; +use span::{Edition, EditionedFileId, FileId, MacroCallId}; use stdx::{format_to, impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasGenericParams, HasName}, @@ -4824,7 +4824,7 @@ impl Type { let iterator_trait = db.lang_item(self.env.krate, LangItem::Iterator)?.as_trait()?; let iterator_item = db .trait_data(iterator_trait) - .associated_type_by_name(&Name::new_symbol(sym::Item.clone(), SyntaxContextId::ROOT))?; + .associated_type_by_name(&Name::new_symbol_root(sym::Item.clone()))?; self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 7f44f396bf..7414735016 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -959,7 +959,10 @@ impl<'db> SemanticsImpl<'db> { process_expansion_for_token(&mut stack, include)?; } None => { - stack.push((file_id.into(), smallvec![(token, SyntaxContextId::ROOT)])); + stack.push(( + file_id.into(), + smallvec![(token, SyntaxContextId::root(file_id.edition()))], + )); } } @@ -1571,7 +1574,7 @@ impl<'db> SemanticsImpl<'db> { self.db.upcast(), &ModPath::from_segments( hir_def::path::PathKind::Plain, - segments.into_iter().map(|it| Name::new(&it, SyntaxContextId::ROOT)), + segments.into_iter().map(|it| Name::new_root(&it)), ), ); Some(items.iter_items().map(|(item, _)| item.into())) diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index e5d871975b..0f126a1a65 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -399,4 +399,38 @@ fn f(s@m::Struct { "#, ) } + + #[test] + fn editions_between_macros() { + check_diagnostics( + r#" +//- /edition2015.rs crate:edition2015 edition:2015 +#[macro_export] +macro_rules! pass_expr_thorough { + ($e:expr) => { $e }; +} + +//- /edition2018.rs crate:edition2018 deps:edition2015 edition:2018 +async fn bar() {} +async fn foo() { + edition2015::pass_expr_thorough!(bar().await); +} + "#, + ); + check_diagnostics( + r#" +//- /edition2018.rs crate:edition2018 edition:2018 +pub async fn bar() {} +#[macro_export] +macro_rules! make_await { + () => { async { $crate::bar().await }; }; +} + +//- /edition2015.rs crate:edition2015 deps:edition2018 edition:2015 +fn foo() { + edition2018::make_await!(); +} + "#, + ); + } } diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 6abf56d4b3..bebd29ef74 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -369,7 +369,8 @@ pub fn expect_fragment<'t>( ) -> ExpandResult> { use ::parser; let buffer = tt_iter.remaining(); - let parser_input = to_parser_input(edition, buffer); + // FIXME: Pass the correct edition per token. Due to the split between mbe and hir-expand it's complicated. + let parser_input = to_parser_input(buffer, &mut |_ctx| edition); let tree_traversal = entry_point.parse(&parser_input, edition); let mut cursor = buffer.cursor(); let mut error = false; diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index e63ad113ff..fb68d35a4c 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs @@ -26,7 +26,7 @@ fn check_( file_id: EditionedFileId::new(FileId::from_raw(0), def_edition), ast_id: ErasedFileAstId::from_raw(0), }, - SyntaxContextId::ROOT, + SyntaxContextId::root(Edition::CURRENT), decl, ) .unwrap(); @@ -39,16 +39,20 @@ fn check_( file_id: EditionedFileId::new(FileId::from_raw(1), call_edition), ast_id: ErasedFileAstId::from_raw(0), }; - let arg_tt = - syntax_bridge::parse_to_token_tree(call_edition, call_anchor, SyntaxContextId::ROOT, arg) - .unwrap(); + let arg_tt = syntax_bridge::parse_to_token_tree( + call_edition, + call_anchor, + SyntaxContextId::root(Edition::CURRENT), + arg, + ) + .unwrap(); let res = mac.expand( &arg_tt, |_| (), Span { range: TextRange::up_to(TextSize::of(arg)), anchor: call_anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }, def_edition, ); @@ -59,7 +63,12 @@ fn check_( if render_debug { format_to!(expect_res, "{:#?}\n\n", res.value.0); } - let (node, _) = syntax_bridge::token_tree_to_syntax_node(&res.value.0, parse, def_edition); + let (node, _) = syntax_bridge::token_tree_to_syntax_node( + &res.value.0, + parse, + &mut |_| def_edition, + def_edition, + ); format_to!( expect_res, "{}", @@ -106,25 +115,25 @@ fn token_mapping_smoke_test() { struct MyTraitMap2 "#, expect![[r#" - SUBTREE $$ 1:0@0..20#0 1:0@0..20#0 - IDENT struct 0:0@34..40#0 - IDENT MyTraitMap2 1:0@8..19#0 - SUBTREE {} 0:0@48..49#0 0:0@100..101#0 - IDENT map 0:0@58..61#0 - PUNCH : [alone] 0:0@61..62#0 - PUNCH : [joint] 0:0@63..64#0 - PUNCH : [alone] 0:0@64..65#0 - IDENT std 0:0@65..68#0 - PUNCH : [joint] 0:0@68..69#0 - PUNCH : [alone] 0:0@69..70#0 - IDENT collections 0:0@70..81#0 - PUNCH : [joint] 0:0@81..82#0 - PUNCH : [alone] 0:0@82..83#0 - IDENT HashSet 0:0@83..90#0 - PUNCH < [alone] 0:0@90..91#0 - SUBTREE () 0:0@91..92#0 0:0@92..93#0 - PUNCH > [joint] 0:0@93..94#0 - PUNCH , [alone] 0:0@94..95#0 + SUBTREE $$ 1:0@0..20#2 1:0@0..20#2 + IDENT struct 0:0@34..40#2 + IDENT MyTraitMap2 1:0@8..19#2 + SUBTREE {} 0:0@48..49#2 0:0@100..101#2 + IDENT map 0:0@58..61#2 + PUNCH : [alone] 0:0@61..62#2 + PUNCH : [joint] 0:0@63..64#2 + PUNCH : [alone] 0:0@64..65#2 + IDENT std 0:0@65..68#2 + PUNCH : [joint] 0:0@68..69#2 + PUNCH : [alone] 0:0@69..70#2 + IDENT collections 0:0@70..81#2 + PUNCH : [joint] 0:0@81..82#2 + PUNCH : [alone] 0:0@82..83#2 + IDENT HashSet 0:0@83..90#2 + PUNCH < [alone] 0:0@90..91#2 + SUBTREE () 0:0@91..92#2 0:0@92..93#2 + PUNCH > [joint] 0:0@93..94#2 + PUNCH , [alone] 0:0@94..95#2 struct MyTraitMap2 { map: ::std::collections::HashSet<()>, @@ -153,28 +162,28 @@ fn main() { } "#, expect![[r#" - SUBTREE $$ 1:0@0..63#0 1:0@0..63#0 - IDENT fn 1:0@1..3#0 - IDENT main 1:0@4..8#0 - SUBTREE () 1:0@8..9#0 1:0@9..10#0 - SUBTREE {} 1:0@11..12#0 1:0@61..62#0 - LITERAL Integer 1 1:0@17..18#0 - PUNCH ; [alone] 1:0@18..19#0 - LITERAL Float 1.0 1:0@24..27#0 - PUNCH ; [alone] 1:0@27..28#0 - SUBTREE () 1:0@33..34#0 1:0@39..40#0 - SUBTREE () 1:0@34..35#0 1:0@37..38#0 - LITERAL Integer 1 1:0@35..36#0 - PUNCH , [alone] 1:0@36..37#0 - PUNCH , [alone] 1:0@38..39#0 - PUNCH . [alone] 1:0@40..41#0 - LITERAL Float 0.0 1:0@41..44#0 - PUNCH ; [alone] 1:0@44..45#0 - IDENT let 1:0@50..53#0 - IDENT x 1:0@54..55#0 - PUNCH = [alone] 1:0@56..57#0 - LITERAL Integer 1 1:0@58..59#0 - PUNCH ; [alone] 1:0@59..60#0 + SUBTREE $$ 1:0@0..63#2 1:0@0..63#2 + IDENT fn 1:0@1..3#2 + IDENT main 1:0@4..8#2 + SUBTREE () 1:0@8..9#2 1:0@9..10#2 + SUBTREE {} 1:0@11..12#2 1:0@61..62#2 + LITERAL Integer 1 1:0@17..18#2 + PUNCH ; [alone] 1:0@18..19#2 + LITERAL Float 1.0 1:0@24..27#2 + PUNCH ; [alone] 1:0@27..28#2 + SUBTREE () 1:0@33..34#2 1:0@39..40#2 + SUBTREE () 1:0@34..35#2 1:0@37..38#2 + LITERAL Integer 1 1:0@35..36#2 + PUNCH , [alone] 1:0@36..37#2 + PUNCH , [alone] 1:0@38..39#2 + PUNCH . [alone] 1:0@40..41#2 + LITERAL Float 0.0 1:0@41..44#2 + PUNCH ; [alone] 1:0@44..45#2 + IDENT let 1:0@50..53#2 + IDENT x 1:0@54..55#2 + PUNCH = [alone] 1:0@56..57#2 + LITERAL Integer 1 1:0@58..59#2 + PUNCH ; [alone] 1:0@59..60#2 fn main(){ 1; @@ -200,14 +209,14 @@ fn expr_2021() { const { 1 }, "#, expect![[r#" - SUBTREE $$ 1:0@0..25#0 1:0@0..25#0 - IDENT _ 1:0@5..6#0 - PUNCH ; [joint] 0:0@36..37#0 - SUBTREE () 0:0@34..35#0 0:0@34..35#0 - IDENT const 1:0@12..17#0 - SUBTREE {} 1:0@18..19#0 1:0@22..23#0 - LITERAL Integer 1 1:0@20..21#0 - PUNCH ; [alone] 0:0@39..40#0 + SUBTREE $$ 1:0@0..25#2 1:0@0..25#2 + IDENT _ 1:0@5..6#2 + PUNCH ; [joint] 0:0@36..37#2 + SUBTREE () 0:0@34..35#2 0:0@34..35#2 + IDENT const 1:0@12..17#2 + SUBTREE {} 1:0@18..19#2 1:0@22..23#2 + LITERAL Integer 1 1:0@20..21#2 + PUNCH ; [alone] 0:0@39..40#2 _; (const { @@ -228,13 +237,13 @@ fn expr_2021() { expect![[r#" ExpandError { inner: ( - 1:0@5..6#0, + 1:0@5..6#2, NoMatchingRule, ), } - SUBTREE $$ 1:0@0..8#0 1:0@0..8#0 - PUNCH ; [alone] 0:0@39..40#0 + SUBTREE $$ 1:0@0..8#2 1:0@0..8#2 + PUNCH ; [alone] 0:0@39..40#2 ;"#]], ); @@ -252,13 +261,13 @@ fn expr_2021() { expect![[r#" ExpandError { inner: ( - 1:0@5..10#0, + 1:0@5..10#2, NoMatchingRule, ), } - SUBTREE $$ 1:0@0..18#0 1:0@0..18#0 - PUNCH ; [alone] 0:0@39..40#0 + SUBTREE $$ 1:0@0..18#2 1:0@0..18#2 + PUNCH ; [alone] 0:0@39..40#2 ;"#]], ); @@ -278,26 +287,26 @@ fn expr_2021() { break 'foo bar, "#, expect![[r#" - SUBTREE $$ 1:0@0..76#0 1:0@0..76#0 - LITERAL Integer 4 1:0@5..6#0 - PUNCH ; [joint] 0:0@41..42#0 - LITERAL Str literal 1:0@12..21#0 - PUNCH ; [joint] 0:0@41..42#0 - SUBTREE () 0:0@39..40#0 0:0@39..40#0 - IDENT funcall 1:0@27..34#0 - SUBTREE () 1:0@34..35#0 1:0@35..36#0 - PUNCH ; [joint] 0:0@41..42#0 - SUBTREE () 0:0@39..40#0 0:0@39..40#0 - IDENT future 1:0@42..48#0 - PUNCH . [alone] 1:0@48..49#0 - IDENT await 1:0@49..54#0 - PUNCH ; [joint] 0:0@41..42#0 - SUBTREE () 0:0@39..40#0 0:0@39..40#0 - IDENT break 1:0@60..65#0 - PUNCH ' [joint] 1:0@66..67#0 - IDENT foo 1:0@67..70#0 - IDENT bar 1:0@71..74#0 - PUNCH ; [alone] 0:0@44..45#0 + SUBTREE $$ 1:0@0..76#2 1:0@0..76#2 + LITERAL Integer 4 1:0@5..6#2 + PUNCH ; [joint] 0:0@41..42#2 + LITERAL Str literal 1:0@12..21#2 + PUNCH ; [joint] 0:0@41..42#2 + SUBTREE () 0:0@39..40#2 0:0@39..40#2 + IDENT funcall 1:0@27..34#2 + SUBTREE () 1:0@34..35#2 1:0@35..36#2 + PUNCH ; [joint] 0:0@41..42#2 + SUBTREE () 0:0@39..40#2 0:0@39..40#2 + IDENT future 1:0@42..48#2 + PUNCH . [alone] 1:0@48..49#2 + IDENT await 1:0@49..54#2 + PUNCH ; [joint] 0:0@41..42#2 + SUBTREE () 0:0@39..40#2 0:0@39..40#2 + IDENT break 1:0@60..65#2 + PUNCH ' [joint] 1:0@66..67#2 + IDENT foo 1:0@67..70#2 + IDENT bar 1:0@71..74#2 + PUNCH ; [alone] 0:0@44..45#2 4; "literal"; @@ -319,13 +328,13 @@ fn expr_2021() { expect![[r#" ExpandError { inner: ( - 1:0@5..6#0, + 1:0@5..6#2, NoMatchingRule, ), } - SUBTREE $$ 1:0@0..8#0 1:0@0..8#0 - PUNCH ; [alone] 0:0@44..45#0 + SUBTREE $$ 1:0@0..8#2 1:0@0..8#2 + PUNCH ; [alone] 0:0@44..45#2 ;"#]], ); diff --git a/crates/proc-macro-api/src/legacy_protocol/msg.rs b/crates/proc-macro-api/src/legacy_protocol/msg.rs index 6ea8db9a90..4b831e4ace 100644 --- a/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -159,7 +159,7 @@ type ProtocolWrite = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) #[cfg(test)] mod tests { use intern::{sym, Symbol}; - use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize}; + use span::{Edition, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize}; use tt::{ Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree, TopSubtreeBuilder, @@ -180,12 +180,12 @@ mod tests { open: Span { range: TextRange::empty(TextSize::new(0)), anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }, close: Span { range: TextRange::empty(TextSize::new(19)), anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }, kind: DelimiterKind::Invisible, }); @@ -196,7 +196,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(0), TextSize::of("struct")), anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }, is_raw: tt::IdentIsRaw::No, } @@ -208,7 +208,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }, is_raw: tt::IdentIsRaw::Yes, } @@ -219,7 +219,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }, kind: tt::LitKind::Str, suffix: None, @@ -229,7 +229,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(13), TextSize::of('@')), anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }, spacing: Spacing::Joint, })); @@ -238,7 +238,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(14), TextSize::of('{')), anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }, ); builder.push(Leaf::Literal(Literal { @@ -246,7 +246,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(15), TextSize::of("0u32")), anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }, kind: tt::LitKind::Integer, suffix: Some(sym::u32.clone()), @@ -254,7 +254,7 @@ mod tests { builder.close(Span { range: TextRange::at(TextSize::new(19), TextSize::of('}')), anchor, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }); builder.build() diff --git a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index beaebf3330..c7614849e0 100644 --- a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -440,7 +440,7 @@ mod tests { file_id: EditionedFileId::current_edition(FileId::from_raw(0)), ast_id: span::ErasedFileAstId::from_raw(0), }, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(span::Edition::CURRENT), }; let s = TokenStream { token_trees: vec![ @@ -482,7 +482,7 @@ mod tests { file_id: EditionedFileId::current_edition(FileId::from_raw(0)), ast_id: span::ErasedFileAstId::from_raw(0), }, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(span::Edition::CURRENT), }; let subtree_paren_a = vec![ tt::TokenTree::Subtree(tt::Subtree { diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs index dc6e71163b..15de88ea65 100644 --- a/crates/proc-macro-srv/src/tests/mod.rs +++ b/crates/proc-macro-srv/src/tests/mod.rs @@ -12,7 +12,7 @@ fn test_derive_empty() { "DeriveEmpty", r#"struct S;"#, expect!["SUBTREE $$ 1 1"], - expect!["SUBTREE $$ 42:2@0..100#0 42:2@0..100#0"], + expect!["SUBTREE $$ 42:2@0..100#2 42:2@0..100#2"], ); } @@ -29,12 +29,12 @@ fn test_derive_error() { LITERAL Str #[derive(DeriveError)] struct S ; 1 PUNCH ; [alone] 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - IDENT compile_error 42:2@0..100#0 - PUNCH ! [alone] 42:2@0..100#0 - SUBTREE () 42:2@0..100#0 42:2@0..100#0 - LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#0 - PUNCH ; [alone] 42:2@0..100#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + IDENT compile_error 42:2@0..100#2 + PUNCH ! [alone] 42:2@0..100#2 + SUBTREE () 42:2@0..100#2 42:2@0..100#2 + LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#2 + PUNCH ; [alone] 42:2@0..100#2"#]], ); } @@ -53,14 +53,14 @@ fn test_fn_like_macro_noop() { PUNCH , [alone] 1 SUBTREE [] 1 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - IDENT ident 42:2@0..5#0 - PUNCH , [alone] 42:2@5..6#0 - LITERAL Integer 0 42:2@7..8#0 - PUNCH , [alone] 42:2@8..9#0 - LITERAL Integer 1 42:2@10..11#0 - PUNCH , [alone] 42:2@11..12#0 - SUBTREE [] 42:2@13..14#0 42:2@14..15#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + IDENT ident 42:2@0..5#2 + PUNCH , [alone] 42:2@5..6#2 + LITERAL Integer 0 42:2@7..8#2 + PUNCH , [alone] 42:2@8..9#2 + LITERAL Integer 1 42:2@10..11#2 + PUNCH , [alone] 42:2@11..12#2 + SUBTREE [] 42:2@13..14#2 42:2@14..15#2"#]], ); } @@ -75,10 +75,10 @@ fn test_fn_like_macro_clone_ident_subtree() { PUNCH , [alone] 1 SUBTREE [] 1 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - IDENT ident 42:2@0..5#0 - PUNCH , [alone] 42:2@5..6#0 - SUBTREE [] 42:2@7..8#0 42:2@7..8#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + IDENT ident 42:2@0..5#2 + PUNCH , [alone] 42:2@5..6#2 + SUBTREE [] 42:2@7..8#2 42:2@7..8#2"#]], ); } @@ -91,8 +91,8 @@ fn test_fn_like_macro_clone_raw_ident() { SUBTREE $$ 1 1 IDENT r#async 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - IDENT r#async 42:2@0..7#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + IDENT r#async 42:2@0..7#2"#]], ); } @@ -105,8 +105,8 @@ fn test_fn_like_fn_like_span_join() { SUBTREE $$ 1 1 IDENT r#joined 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - IDENT r#joined 42:2@0..11#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + IDENT r#joined 42:2@0..11#2"#]], ); } @@ -121,10 +121,10 @@ fn test_fn_like_fn_like_span_ops() { IDENT resolved_at_def_site 1 IDENT start_span 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - IDENT set_def_site 41:1@0..150#0 - IDENT resolved_at_def_site 42:2@13..33#0 - IDENT start_span 42:2@34..34#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + IDENT set_def_site 41:1@0..150#2 + IDENT resolved_at_def_site 42:2@13..33#2 + IDENT start_span 42:2@34..34#2"#]], ); } @@ -143,14 +143,14 @@ fn test_fn_like_mk_literals() { LITERAL Integer 123i64 1 LITERAL Integer 123 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - LITERAL ByteStr byte_string 42:2@0..100#0 - LITERAL Char c 42:2@0..100#0 - LITERAL Str string 42:2@0..100#0 - LITERAL Float 3.14f64 42:2@0..100#0 - LITERAL Float 3.14 42:2@0..100#0 - LITERAL Integer 123i64 42:2@0..100#0 - LITERAL Integer 123 42:2@0..100#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + LITERAL ByteStr byte_string 42:2@0..100#2 + LITERAL Char c 42:2@0..100#2 + LITERAL Str string 42:2@0..100#2 + LITERAL Float 3.14f64 42:2@0..100#2 + LITERAL Float 3.14 42:2@0..100#2 + LITERAL Integer 123i64 42:2@0..100#2 + LITERAL Integer 123 42:2@0..100#2"#]], ); } @@ -164,9 +164,9 @@ fn test_fn_like_mk_idents() { IDENT standard 1 IDENT r#raw 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - IDENT standard 42:2@0..100#0 - IDENT r#raw 42:2@0..100#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + IDENT standard 42:2@0..100#2 + IDENT r#raw 42:2@0..100#2"#]], ); } @@ -198,27 +198,27 @@ fn test_fn_like_macro_clone_literals() { PUNCH , [alone] 1 LITERAL CStr null 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - LITERAL Integer 1u16 42:2@0..4#0 - PUNCH , [alone] 42:2@4..5#0 - LITERAL Integer 2_u32 42:2@6..11#0 - PUNCH , [alone] 42:2@11..12#0 - PUNCH - [alone] 42:2@13..14#0 - LITERAL Integer 4i64 42:2@14..18#0 - PUNCH , [alone] 42:2@18..19#0 - LITERAL Float 3.14f32 42:2@20..27#0 - PUNCH , [alone] 42:2@27..28#0 - LITERAL Str hello bridge 42:2@29..43#0 - PUNCH , [alone] 42:2@43..44#0 - LITERAL Str suffixedsuffix 42:2@45..61#0 - PUNCH , [alone] 42:2@61..62#0 - LITERAL StrRaw(2) raw 42:2@63..73#0 - PUNCH , [alone] 42:2@73..74#0 - LITERAL Char a 42:2@75..78#0 - PUNCH , [alone] 42:2@78..79#0 - LITERAL Byte b 42:2@80..84#0 - PUNCH , [alone] 42:2@84..85#0 - LITERAL CStr null 42:2@86..93#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + LITERAL Integer 1u16 42:2@0..4#2 + PUNCH , [alone] 42:2@4..5#2 + LITERAL Integer 2_u32 42:2@6..11#2 + PUNCH , [alone] 42:2@11..12#2 + PUNCH - [alone] 42:2@13..14#2 + LITERAL Integer 4i64 42:2@14..18#2 + PUNCH , [alone] 42:2@18..19#2 + LITERAL Float 3.14f32 42:2@20..27#2 + PUNCH , [alone] 42:2@27..28#2 + LITERAL Str hello bridge 42:2@29..43#2 + PUNCH , [alone] 42:2@43..44#2 + LITERAL Str suffixedsuffix 42:2@45..61#2 + PUNCH , [alone] 42:2@61..62#2 + LITERAL StrRaw(2) raw 42:2@63..73#2 + PUNCH , [alone] 42:2@73..74#2 + LITERAL Char a 42:2@75..78#2 + PUNCH , [alone] 42:2@78..79#2 + LITERAL Byte b 42:2@80..84#2 + PUNCH , [alone] 42:2@84..85#2 + LITERAL CStr null 42:2@86..93#2"#]], ); } @@ -239,12 +239,12 @@ fn test_attr_macro() { LITERAL Str #[attr_error(some arguments)] mod m {} 1 PUNCH ; [alone] 1"#]], expect![[r#" - SUBTREE $$ 42:2@0..100#0 42:2@0..100#0 - IDENT compile_error 42:2@0..100#0 - PUNCH ! [alone] 42:2@0..100#0 - SUBTREE () 42:2@0..100#0 42:2@0..100#0 - LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#0 - PUNCH ; [alone] 42:2@0..100#0"#]], + SUBTREE $$ 42:2@0..100#2 42:2@0..100#2 + IDENT compile_error 42:2@0..100#2 + PUNCH ! [alone] 42:2@0..100#2 + SUBTREE () 42:2@0..100#2 42:2@0..100#2 + LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#2 + PUNCH ; [alone] 42:2@0..100#2"#]], ); } diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs index 37d51050f3..42f0a7f59b 100644 --- a/crates/proc-macro-srv/src/tests/utils.rs +++ b/crates/proc-macro-srv/src/tests/utils.rs @@ -76,7 +76,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ErasedFileAstId::from_raw(1), }, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(span::Edition::CURRENT), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -84,7 +84,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ErasedFileAstId::from_raw(2), }, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(span::Edition::CURRENT), }; let mixed_site = call_site; diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs index 87a948df55..6becc8e41e 100644 --- a/crates/span/src/hygiene.rs +++ b/crates/span/src/hygiene.rs @@ -26,7 +26,7 @@ use crate::InternId; #[cfg(feature = "ra-salsa")] use ra_salsa::{InternId, InternValue}; -use crate::MacroCallId; +use crate::{Edition, MacroCallId}; /// Interned [`SyntaxContextData`]. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -59,11 +59,20 @@ impl fmt::Display for SyntaxContextId { } impl SyntaxContextId { + #[inline] + pub fn remove_root_edition(&mut self) { + if self.is_root() { + *self = Self::root(Edition::Edition2015); + } + } + /// The root context, which is the parent of all other contexts. All [`FileId`]s have this context. - pub const ROOT: Self = SyntaxContextId(unsafe { InternId::new_unchecked(0) }); + pub const fn root(edition: Edition) -> Self { + SyntaxContextId(unsafe { InternId::new_unchecked(edition as u32) }) + } pub fn is_root(self) -> bool { - self == Self::ROOT + self.into_u32() <= Edition::LATEST as u32 } /// Deconstruct a `SyntaxContextId` into a raw `u32`. @@ -89,6 +98,7 @@ pub struct SyntaxContextData { // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent. pub outer_expn: Option, pub outer_transparency: Transparency, + pub edition: Edition, pub parent: SyntaxContextId, /// This context, but with all transparent and semi-transparent expansions filtered away. pub opaque: SyntaxContextId, @@ -98,10 +108,10 @@ pub struct SyntaxContextData { #[cfg(feature = "ra-salsa")] impl InternValue for SyntaxContextData { - type Key = (SyntaxContextId, Option, Transparency); + type Key = (SyntaxContextId, Option, Transparency, Edition); fn into_key(&self) -> Self::Key { - (self.parent, self.outer_expn, self.outer_transparency) + (self.parent, self.outer_expn, self.outer_transparency, self.edition) } } @@ -118,13 +128,14 @@ impl std::fmt::Debug for SyntaxContextData { } impl SyntaxContextData { - pub fn root() -> Self { + pub fn root(edition: Edition) -> Self { SyntaxContextData { outer_expn: None, outer_transparency: Transparency::Opaque, - parent: SyntaxContextId::ROOT, - opaque: SyntaxContextId::ROOT, - opaque_and_semitransparent: SyntaxContextId::ROOT, + parent: SyntaxContextId::root(edition), + opaque: SyntaxContextId::root(edition), + opaque_and_semitransparent: SyntaxContextId::root(edition), + edition, } } } diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs index 66bbce1859..dc35de67fd 100644 --- a/crates/span/src/map.rs +++ b/crates/span/src/map.rs @@ -208,7 +208,7 @@ impl RealSpanMap { Span { range: range - offset, anchor: SpanAnchor { file_id: self.file_id, ast_id }, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(self.file_id.edition()), } } } diff --git a/crates/syntax-bridge/src/lib.rs b/crates/syntax-bridge/src/lib.rs index ed8b1908d6..19801c49e4 100644 --- a/crates/syntax-bridge/src/lib.rs +++ b/crates/syntax-bridge/src/lib.rs @@ -1,6 +1,6 @@ //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`]. -use std::fmt; +use std::{fmt, hash::Hash}; use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; @@ -58,7 +58,7 @@ pub mod dummy_test_span_utils { ), ast_id: span::ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), }; pub struct DummyTestSpanMap; @@ -74,7 +74,7 @@ pub mod dummy_test_span_utils { ), ast_id: span::ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContextId::ROOT, + ctx: SyntaxContextId::root(Edition::CURRENT), } } } @@ -141,15 +141,16 @@ where pub fn token_tree_to_syntax_node( tt: &tt::TopSubtree>, entry_point: parser::TopEntryPoint, - edition: parser::Edition, + span_to_edition: &mut dyn FnMut(Ctx) -> Edition, + top_edition: Edition, ) -> (Parse, SpanMap) where - SpanData: Copy + fmt::Debug, - Ctx: PartialEq, + Ctx: Copy + fmt::Debug + PartialEq + PartialEq + Eq + Hash, { let buffer = tt.view().strip_invisible(); - let parser_input = to_parser_input(edition, buffer); - let parser_output = entry_point.parse(&parser_input, edition); + let parser_input = to_parser_input(buffer, span_to_edition); + // It matters what edition we parse with even when we escape all identifiers correctly. + let parser_output = entry_point.parse(&parser_input, top_edition); let mut tree_sink = TtTreeSink::new(buffer.cursor()); for event in parser_output.iter() { match event { diff --git a/crates/syntax-bridge/src/to_parser_input.rs b/crates/syntax-bridge/src/to_parser_input.rs index 1bbb05f550..0dcb2be316 100644 --- a/crates/syntax-bridge/src/to_parser_input.rs +++ b/crates/syntax-bridge/src/to_parser_input.rs @@ -2,17 +2,20 @@ //! format that works for our parser. use std::fmt; +use std::hash::Hash; -use span::Edition; +use rustc_hash::FxHashMap; +use span::{Edition, SpanData}; use syntax::{SyntaxKind, SyntaxKind::*, T}; -pub fn to_parser_input( - edition: Edition, - buffer: tt::TokenTreesView<'_, S>, +pub fn to_parser_input( + buffer: tt::TokenTreesView<'_, SpanData>, + span_to_edition: &mut dyn FnMut(Ctx) -> Edition, ) -> parser::Input { let mut res = parser::Input::default(); let mut current = buffer.cursor(); + let mut syntax_context_to_edition_cache = FxHashMap::default(); while !current.eof() { let tt = current.token_tree(); @@ -57,20 +60,25 @@ pub fn to_parser_input( res.was_joint(); } } - tt::Leaf::Ident(ident) => match ident.sym.as_str() { - "_" => res.push(T![_]), - i if i.starts_with('\'') => res.push(LIFETIME_IDENT), - _ if ident.is_raw.yes() => res.push(IDENT), - text => match SyntaxKind::from_keyword(text, edition) { - Some(kind) => res.push(kind), - None => { - let contextual_keyword = - SyntaxKind::from_contextual_keyword(text, edition) - .unwrap_or(SyntaxKind::IDENT); - res.push_ident(contextual_keyword); - } - }, - }, + tt::Leaf::Ident(ident) => { + let edition = *syntax_context_to_edition_cache + .entry(ident.span.ctx) + .or_insert_with(|| span_to_edition(ident.span.ctx)); + match ident.sym.as_str() { + "_" => res.push(T![_]), + i if i.starts_with('\'') => res.push(LIFETIME_IDENT), + _ if ident.is_raw.yes() => res.push(IDENT), + text => match SyntaxKind::from_keyword(text, edition) { + Some(kind) => res.push(kind), + None => { + let contextual_keyword = + SyntaxKind::from_contextual_keyword(text, edition) + .unwrap_or(SyntaxKind::IDENT); + res.push_ident(contextual_keyword); + } + }, + } + } tt::Leaf::Punct(punct) => { let kind = SyntaxKind::from_char(punct.char) .unwrap_or_else(|| panic!("{punct:#?} is not a valid punct")); From 646e96f68df6ab9cf55a53b466c096315e73a1ec Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 9 Jan 2025 09:37:54 +0100 Subject: [PATCH 27/98] minor: Fixup macro error kinds --- crates/hir-expand/src/lib.rs | 6 ++++-- crates/ide-diagnostics/src/handlers/macro_error.rs | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index a0c4c125db..2c664029f6 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -188,6 +188,8 @@ impl fmt::Display for RenderedExpandError { impl RenderedExpandError { const GENERAL_KIND: &str = "macro-error"; + const DISABLED: &str = "proc-macro-disabled"; + const ATTR_EXP_DISABLED: &str = "attribute-expansion-disabled"; } impl ExpandErrorKind { @@ -196,12 +198,12 @@ impl ExpandErrorKind { ExpandErrorKind::ProcMacroAttrExpansionDisabled => RenderedExpandError { message: "procedural attribute macro expansion is disabled".to_owned(), error: false, - kind: "proc-macros-disabled", + kind: RenderedExpandError::ATTR_EXP_DISABLED, }, ExpandErrorKind::MacroDisabled => RenderedExpandError { message: "proc-macro is explicitly disabled".to_owned(), error: false, - kind: "proc-macro-disabled", + kind: RenderedExpandError::DISABLED, }, &ExpandErrorKind::MissingProcMacroExpander(def_crate) => { match db.proc_macros().get_error_for_crate(def_crate) { diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index e177b72e4d..edf656ed04 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -4,13 +4,13 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // // This diagnostic is shown for macro expansion errors. -// Diagnostic: proc-macros-disabled +// Diagnostic: attribute-expansion-disabled // -// This diagnostic is shown for proc macros where proc macros have been disabled. +// This diagnostic is shown for attribute proc macros when attribute expansions have been disabled. // Diagnostic: proc-macro-disabled // -// This diagnostic is shown for proc macros that has been specifically disabled via `rust-analyzer.procMacro.ignored`. +// This diagnostic is shown for proc macros that have been specifically disabled via `rust-analyzer.procMacro.ignored`. pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic { // Use more accurate position if available. let display_range = ctx.resolve_precise_location(&d.node, d.precise_location); From 04c06b4c5a24686d52014eebf2e1c0cb3d41403b Mon Sep 17 00:00:00 2001 From: duncan Date: Thu, 9 Jan 2025 16:01:08 +0000 Subject: [PATCH 28/98] Fix parsing cargo test json output by making stdout and optional field --- crates/rust-analyzer/src/test_runner.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/test_runner.rs b/crates/rust-analyzer/src/test_runner.rs index 503b3ee43a..3edfb812cf 100644 --- a/crates/rust-analyzer/src/test_runner.rs +++ b/crates/rust-analyzer/src/test_runner.rs @@ -18,7 +18,11 @@ pub(crate) enum TestState { Started, Ok, Ignored, - Failed { stdout: String }, + Failed { + // the stdout field is not always present depending on cargo test flags + #[serde(skip_serializing_if = "String::is_empty", default)] + stdout: String, + }, } #[derive(Debug, Deserialize)] From 5ffe45d8cd768e56abcf4a42f0f2a6a763dc21e4 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:12:04 -0500 Subject: [PATCH 29/98] Add a new and improved syntax tree viewer --- crates/ide/src/lib.rs | 5 + crates/ide/src/syntax_tree.rs | 4 +- crates/ide/src/view_syntax_tree.rs | 226 ++++++++++++++ crates/rust-analyzer/src/handlers/request.rs | 10 + crates/rust-analyzer/src/lsp/ext.rs | 14 + crates/rust-analyzer/src/main_loop.rs | 1 + docs/dev/lsp-extensions.md | 19 +- editors/code/package.json | 70 +++++ editors/code/src/commands.ts | 33 ++ editors/code/src/config.ts | 4 + editors/code/src/ctx.ts | 69 ++++- editors/code/src/lsp_ext.ts | 4 + editors/code/src/main.ts | 3 + editors/code/src/syntax_tree_provider.ts | 301 +++++++++++++++++++ 14 files changed, 759 insertions(+), 4 deletions(-) create mode 100644 crates/ide/src/view_syntax_tree.rs create mode 100644 editors/code/src/syntax_tree_provider.ts diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 6e7c718953..5cb77e6201 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -56,6 +56,7 @@ mod view_hir; mod view_item_tree; mod view_memory_layout; mod view_mir; +mod view_syntax_tree; use std::{iter, panic::UnwindSafe}; @@ -339,6 +340,10 @@ impl Analysis { self.with_db(|db| syntax_tree::syntax_tree(db, file_id, text_range)) } + pub fn view_syntax_tree(&self, file_id: FileId) -> Cancellable { + self.with_db(|db| view_syntax_tree::view_syntax_tree(db, file_id)) + } + pub fn view_hir(&self, position: FilePosition) -> Cancellable { self.with_db(|db| view_hir::view_hir(db, position)) } diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs index e241cb82bd..86c6676f92 100644 --- a/crates/ide/src/syntax_tree.rs +++ b/crates/ide/src/syntax_tree.rs @@ -4,9 +4,9 @@ use syntax::{ AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize, }; -// Feature: Show Syntax Tree +// Feature: Show Debug Syntax Tree // -// Shows the parse tree of the current file. It exists mostly for debugging +// Shows the textual parse tree of the current file. It exists mostly for debugging // rust-analyzer itself. // // |=== diff --git a/crates/ide/src/view_syntax_tree.rs b/crates/ide/src/view_syntax_tree.rs new file mode 100644 index 0000000000..b2adff0f36 --- /dev/null +++ b/crates/ide/src/view_syntax_tree.rs @@ -0,0 +1,226 @@ +use hir::Semantics; +use ide_db::{FileId, RootDatabase}; +use span::TextRange; +use stdx::format_to; +use syntax::{ + ast::{self, IsString}, + AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, WalkEvent, +}; + +// Feature: Show Syntax Tree +// +// Shows a tree view with the syntax tree of the current file +// +// |=== +// | Editor | Panel Name +// +// | VS Code | **Rust Syntax Tree** +// |=== +pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String { + let sema = Semantics::new(db); + let parse = sema.parse_guess_edition(file_id); + syntax_node_to_json(parse.syntax(), None) +} + +fn syntax_node_to_json(node: &SyntaxNode, ctx: Option) -> String { + let mut result = String::new(); + for event in node.preorder_with_tokens() { + match event { + WalkEvent::Enter(it) => { + let kind = it.kind(); + let (text_range, inner_range_str) = match &ctx { + Some(ctx) => { + let inner_start: u32 = it.text_range().start().into(); + let inner_end: u32 = it.text_range().end().into(); + + let mut true_start = inner_start + ctx.offset; + let mut true_end = inner_end + ctx.offset; + for pos in &ctx.marker_positions { + if *pos >= inner_end { + break; + } + + // We conditionally add to true_start in case + // the marker is between the start and end. + true_start += 2 * (*pos < inner_start) as u32; + true_end += 2; + } + + let true_range = TextRange::new(true_start.into(), true_end.into()); + + ( + true_range, + format!( + r#","istart":{:?},"iend":{:?}"#, + it.text_range().start(), + it.text_range().end() + ), + ) + } + None => (it.text_range(), "".to_owned()), + }; + let start = text_range.start(); + let end = text_range.end(); + + match it { + NodeOrToken::Node(_) => { + format_to!( + result, + r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":["# + ); + } + NodeOrToken::Token(token) => { + let comma = if token.next_sibling_or_token().is_some() { "," } else { "" }; + match parse_rust_string(token) { + Some(parsed) => { + format_to!( + result, + r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":[{parsed}]}}{comma}"# + ); + } + None => format_to!( + result, + r#"{{"type":"Token","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str}}}{comma}"# + ), + } + } + } + } + WalkEvent::Leave(it) => match it { + NodeOrToken::Node(node) => { + let comma = if node.next_sibling_or_token().is_some() { "," } else { "" }; + format_to!(result, "]}}{comma}") + } + NodeOrToken::Token(_) => (), + }, + } + } + + result +} + +fn parse_rust_string(token: SyntaxToken) -> Option { + let string_node = ast::String::cast(token)?; + let text = string_node.value().ok()?; + + let mut trim_result = String::new(); + let mut marker_positions = Vec::new(); + let mut skipped = 0; + let mut last_end = 0; + for (start, part) in text.match_indices("$0") { + marker_positions.push((start - skipped) as u32); + trim_result.push_str(&text[last_end..start]); + skipped += part.len(); + last_end = start + part.len(); + } + trim_result.push_str(&text[last_end..text.len()]); + + let parsed = SourceFile::parse(&trim_result, span::Edition::CURRENT); + + if !parsed.errors().is_empty() { + return None; + } + + let node: &SyntaxNode = &parsed.syntax_node(); + + if node.children().count() == 0 { + // C'mon, you should have at least one node other than SOURCE_FILE + return None; + } + + Some(syntax_node_to_json( + node, + Some(InStringCtx { + offset: string_node.text_range_between_quotes()?.start().into(), + marker_positions, + }), + )) +} + +struct InStringCtx { + offset: u32, + marker_positions: Vec, +} + +#[cfg(test)] +mod tests { + use expect_test::expect; + + use crate::fixture; + + fn check(ra_fixture: &str, expect: expect_test::Expect) { + let (analysis, file_id) = fixture::file(ra_fixture); + let syn = analysis.view_syntax_tree(file_id).unwrap(); + expect.assert_eq(&syn) + } + + #[test] + fn view_syntax_tree() { + // Basic syntax + check( + r#"fn foo() {}"#, + expect![[ + r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":11,"children":[{"type":"Node","kind":"FN","start":0,"end":11,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":6,"children":[{"type":"Token","kind":"IDENT","start":3,"end":6}]},{"type":"Node","kind":"PARAM_LIST","start":6,"end":8,"children":[{"type":"Token","kind":"L_PAREN","start":6,"end":7},{"type":"Token","kind":"R_PAREN","start":7,"end":8}]},{"type":"Token","kind":"WHITESPACE","start":8,"end":9},{"type":"Node","kind":"BLOCK_EXPR","start":9,"end":11,"children":[{"type":"Node","kind":"STMT_LIST","start":9,"end":11,"children":[{"type":"Token","kind":"L_CURLY","start":9,"end":10},{"type":"Token","kind":"R_CURLY","start":10,"end":11}]}]}]}]}"# + ]], + ); + + check( + r#" +fn test() { + assert!(" + fn foo() { + } + ", ""); +}"#, + expect![[ + r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":60,"children":[{"type":"Node","kind":"FN","start":0,"end":60,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":60,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":60,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":58,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":57,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":57,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":57,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":52,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":51,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":30,"istart":0,"iend":5},{"type":"Node","kind":"FN","start":30,"end":46,"istart":5,"iend":21,"children":[{"type":"Token","kind":"FN_KW","start":30,"end":32,"istart":5,"iend":7},{"type":"Token","kind":"WHITESPACE","start":32,"end":33,"istart":7,"iend":8},{"type":"Node","kind":"NAME","start":33,"end":36,"istart":8,"iend":11,"children":[{"type":"Token","kind":"IDENT","start":33,"end":36,"istart":8,"iend":11}]},{"type":"Node","kind":"PARAM_LIST","start":36,"end":38,"istart":11,"iend":13,"children":[{"type":"Token","kind":"L_PAREN","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_PAREN","start":37,"end":38,"istart":12,"iend":13}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"BLOCK_EXPR","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Node","kind":"STMT_LIST","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Token","kind":"L_CURLY","start":39,"end":40,"istart":14,"iend":15},{"type":"Token","kind":"WHITESPACE","start":40,"end":45,"istart":15,"iend":20},{"type":"Token","kind":"R_CURLY","start":45,"end":46,"istart":20,"iend":21}]}]}]},{"type":"Token","kind":"WHITESPACE","start":46,"end":51,"istart":21,"iend":26}]}]},{"type":"Token","kind":"COMMA","start":52,"end":53},{"type":"Token","kind":"WHITESPACE","start":53,"end":54},{"type":"Token","kind":"STRING","start":54,"end":56},{"type":"Token","kind":"R_PAREN","start":56,"end":57}]}]}]},{"type":"Token","kind":"SEMICOLON","start":57,"end":58}]},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"R_CURLY","start":59,"end":60}]}]}]}]}"# + ]], + ) + } + + #[test] + fn view_syntax_tree_inside_string() { + check( + r#"fn test() { + assert!(" +$0fn foo() { +}$0 +fn bar() { +} + ", ""); +}"#, + expect![[ + r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":65,"children":[{"type":"Node","kind":"FN","start":0,"end":65,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":65,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":65,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":63,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":62,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":62,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":62,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":57,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":56,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":26,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":26,"end":38,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":26,"end":28,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":28,"end":29,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":29,"end":32,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":29,"end":32,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":32,"end":34,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":32,"end":33,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":33,"end":34,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":34,"end":35,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":35,"end":36,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":37,"end":38,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":39,"end":51,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":39,"end":41,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":41,"end":42,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":42,"end":45,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":42,"end":45,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":45,"end":47,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":45,"end":46,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":46,"end":47,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":47,"end":48,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":48,"end":49,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":50,"end":51,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":51,"end":56,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":57,"end":58},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"STRING","start":59,"end":61},{"type":"Token","kind":"R_PAREN","start":61,"end":62}]}]}]},{"type":"Token","kind":"SEMICOLON","start":62,"end":63}]},{"type":"Token","kind":"WHITESPACE","start":63,"end":64},{"type":"Token","kind":"R_CURLY","start":64,"end":65}]}]}]}]}"# + ]], + ); + + // With a raw string + check( + r###"fn test() { + assert!(r#" +$0fn foo() { +}$0 +fn bar() { +} + "#, ""); +}"###, + expect![[ + r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":68,"children":[{"type":"Node","kind":"FN","start":0,"end":68,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":68,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":68,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":66,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":65,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":65,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":65,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":60,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":58,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":53,"end":58,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":60,"end":61},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"STRING","start":62,"end":64},{"type":"Token","kind":"R_PAREN","start":64,"end":65}]}]}]},{"type":"Token","kind":"SEMICOLON","start":65,"end":66}]},{"type":"Token","kind":"WHITESPACE","start":66,"end":67},{"type":"Token","kind":"R_CURLY","start":67,"end":68}]}]}]}]}"# + ]], + ); + + // With a raw string + check( + r###"fn test() { + assert!(r$0#" +fn foo() { +} +fn bar() { +}"$0#, ""); +}"###, + expect![[ + r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":63,"children":[{"type":"Node","kind":"FN","start":0,"end":63,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":63,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":63,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":61,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":60,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":60,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":60,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":55,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":53,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":55,"end":56},{"type":"Token","kind":"WHITESPACE","start":56,"end":57},{"type":"Token","kind":"STRING","start":57,"end":59},{"type":"Token","kind":"R_PAREN","start":59,"end":60}]}]}]},{"type":"Token","kind":"SEMICOLON","start":60,"end":61}]},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"R_CURLY","start":62,"end":63}]}]}]}]}"# + ]], + ); + } +} diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 7ac70efe2d..4644ebd44d 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -148,6 +148,16 @@ pub(crate) fn handle_syntax_tree( Ok(res) } +pub(crate) fn handle_view_syntax_tree( + snap: GlobalStateSnapshot, + params: lsp_ext::ViewSyntaxTreeParams, +) -> anyhow::Result { + let _p = tracing::info_span!("handle_view_syntax_tree").entered(); + let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let res = snap.analysis.view_syntax_tree(id)?; + Ok(res) +} + pub(crate) fn handle_view_hir( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs index f50cbba7ac..48c2ff0a15 100644 --- a/crates/rust-analyzer/src/lsp/ext.rs +++ b/crates/rust-analyzer/src/lsp/ext.rs @@ -123,6 +123,20 @@ pub struct SyntaxTreeParams { pub range: Option, } +pub enum ViewSyntaxTree {} + +impl Request for ViewSyntaxTree { + type Params = ViewSyntaxTreeParams; + type Result = String; + const METHOD: &'static str = "rust-analyzer/viewSyntaxTree"; +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ViewSyntaxTreeParams { + pub text_document: TextDocumentIdentifier, +} + pub enum ViewHir {} impl Request for ViewHir { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 97657b9265..325b0afc71 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -1146,6 +1146,7 @@ impl GlobalState { .on::(handlers::handle_ssr) .on::(handlers::handle_view_recursive_memory_layout) .on::(handlers::handle_syntax_tree) + .on::(handlers::handle_view_syntax_tree) .on::(handlers::handle_view_hir) .on::(handlers::handle_view_mir) .on::(handlers::handle_interpret_function) diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 21ac3a5a26..8cd0422773 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@