Skip to content

Commit 05df6c5

Browse files
committed
Auto merge of #16027 - Veykril:implicit-format-args, r=Veykril
feat: Implicit format args support Fixes #11260 Fixes #11296 ![image](https://github.com/rust-lang/rust-analyzer/assets/3757771/14fe2caf-4ea3-40a5-8aa4-ff08ea0ccbde) Too lazy to make a gif of this right now (would probably be good to show renaming)
2 parents afc1ae1 + 9b7ec5e commit 05df6c5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+921
-331
lines changed

crates/hir-def/src/body.rs

+12
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ pub struct BodySourceMap {
9595
field_map_back: FxHashMap<ExprId, FieldSource>,
9696
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
9797

98+
format_args_template_map: FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
99+
98100
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
99101

100102
/// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
@@ -387,6 +389,14 @@ impl BodySourceMap {
387389
self.expr_map.get(&src).copied()
388390
}
389391

392+
pub fn implicit_format_args(
393+
&self,
394+
node: InFile<&ast::FormatArgsExpr>,
395+
) -> Option<&[(syntax::TextRange, Name)]> {
396+
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
397+
self.format_args_template_map.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref)
398+
}
399+
390400
/// Get a reference to the body source map's diagnostics.
391401
pub fn diagnostics(&self) -> &[BodyDiagnostic] {
392402
&self.diagnostics
@@ -403,8 +413,10 @@ impl BodySourceMap {
403413
field_map_back,
404414
pat_field_map_back,
405415
expansions,
416+
format_args_template_map,
406417
diagnostics,
407418
} = self;
419+
format_args_template_map.shrink_to_fit();
408420
expr_map.shrink_to_fit();
409421
expr_map_back.shrink_to_fit();
410422
pat_map.shrink_to_fit();

crates/hir-def/src/body/lower.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -1597,12 +1597,20 @@ impl ExprCollector<'_> {
15971597
});
15981598
let template = f.template();
15991599
let fmt_snippet = template.as_ref().map(ToString::to_string);
1600+
let mut mappings = vec![];
16001601
let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) {
1601-
Some((s, is_direct_literal)) => {
1602-
format_args::parse(&s, fmt_snippet, args, is_direct_literal, |name| {
1603-
self.alloc_expr_desugared(Expr::Path(Path::from(name)))
1604-
})
1605-
}
1602+
Some((s, is_direct_literal)) => format_args::parse(
1603+
&s,
1604+
fmt_snippet,
1605+
args,
1606+
is_direct_literal,
1607+
|name| self.alloc_expr_desugared(Expr::Path(Path::from(name))),
1608+
|name, span| {
1609+
if let Some(span) = span {
1610+
mappings.push((span, name.clone()))
1611+
}
1612+
},
1613+
),
16061614
None => FormatArgs { template: Default::default(), arguments: args.finish() },
16071615
};
16081616

@@ -1746,14 +1754,16 @@ impl ExprCollector<'_> {
17461754
tail: Some(unsafe_arg_new),
17471755
});
17481756

1749-
self.alloc_expr(
1757+
let idx = self.alloc_expr(
17501758
Expr::Call {
17511759
callee: new_v1_formatted,
17521760
args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
17531761
is_assignee_expr: false,
17541762
},
17551763
syntax_ptr,
1756-
)
1764+
);
1765+
self.source_map.format_args_template_map.insert(idx, mappings);
1766+
idx
17571767
}
17581768

17591769
/// Generate a hir expression for a format_args placeholder specification.

crates/hir-def/src/body/tests.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ fn main() {
160160
let count = 10;
161161
builtin#lang(Arguments::new_v1_formatted)(
162162
&[
163-
"\"hello ", " ", " friends, we ", " ", "", "\"",
163+
"hello ", " ", " friends, we ", " ", "",
164164
],
165165
&[
166166
builtin#lang(Argument::new_display)(
@@ -261,7 +261,7 @@ impl SsrError {
261261
_ = $crate::error::SsrError::new(
262262
builtin#lang(Arguments::new_v1_formatted)(
263263
&[
264-
"\"Failed to resolve path `", "`\"",
264+
"Failed to resolve path `", "`",
265265
],
266266
&[
267267
builtin#lang(Argument::new_display)(
@@ -320,7 +320,7 @@ fn f() {
320320
$crate::panicking::panic_fmt(
321321
builtin#lang(Arguments::new_v1_formatted)(
322322
&[
323-
"\"cc\"",
323+
"cc",
324324
],
325325
&[],
326326
&[],

crates/hir-def/src/hir/format_args.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use hir_expand::name::Name;
55
use rustc_dependencies::parse_format as parse;
66
use syntax::{
77
ast::{self, IsString},
8-
AstToken, SmolStr, TextRange,
8+
SmolStr, TextRange, TextSize,
99
};
1010

1111
use crate::hir::ExprId;
@@ -170,15 +170,18 @@ pub(crate) fn parse(
170170
mut args: FormatArgumentsCollector,
171171
is_direct_literal: bool,
172172
mut synth: impl FnMut(Name) -> ExprId,
173+
mut record_usage: impl FnMut(Name, Option<TextRange>),
173174
) -> FormatArgs {
174-
let text = s.text();
175+
let text = s.text_without_quotes();
175176
let str_style = match s.quote_offsets() {
176177
Some(offsets) => {
177178
let raw = u32::from(offsets.quotes.0.len()) - 1;
178-
(raw != 0).then_some(raw as usize)
179+
// subtract 1 for the `r` prefix
180+
(raw != 0).then(|| raw as usize - 1)
179181
}
180182
None => None,
181183
};
184+
182185
let mut parser =
183186
parse::Parser::new(text, str_style, fmt_snippet, false, parse::ParseMode::Format);
184187

@@ -199,6 +202,7 @@ pub(crate) fn parse(
199202
let to_span = |inner_span: parse::InnerSpan| {
200203
is_source_literal.then(|| {
201204
TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
205+
- TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1)
202206
})
203207
};
204208

@@ -230,9 +234,10 @@ pub(crate) fn parse(
230234
Err(index)
231235
}
232236
}
233-
ArgRef::Name(name, _span) => {
237+
ArgRef::Name(name, span) => {
234238
let name = Name::new_text_dont_use(SmolStr::new(name));
235239
if let Some((index, _)) = args.by_name(&name) {
240+
record_usage(name, span);
236241
// Name found in `args`, so we resolve it to its index.
237242
if index < args.explicit_args().len() {
238243
// Mark it as used, if it was an explicit argument.
@@ -246,6 +251,7 @@ pub(crate) fn parse(
246251
// disabled (see RFC #2795)
247252
// FIXME: Diagnose
248253
}
254+
record_usage(name.clone(), span);
249255
Ok(args.add(FormatArgument {
250256
kind: FormatArgumentKind::Captured(name.clone()),
251257
// FIXME: This is problematic, we might want to synthesize a dummy

crates/hir-expand/src/lib.rs

-2
Original file line numberDiff line numberDiff line change
@@ -629,8 +629,6 @@ impl ExpansionInfo {
629629
pub fn map_range_down<'a>(
630630
&'a self,
631631
span: SpanData,
632-
// FIXME: use this for range mapping, so that we can resolve inline format args
633-
_relative_token_offset: Option<TextSize>,
634632
) -> Option<impl Iterator<Item = InMacroFile<SyntaxToken>> + 'a> {
635633
let tokens = self
636634
.exp_map

crates/hir/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ pub use crate::{
9292
attrs::{resolve_doc_path_on, HasAttrs},
9393
diagnostics::*,
9494
has_source::HasSource,
95-
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
95+
semantics::{
96+
DescendPreference, PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits,
97+
},
9698
};
9799

98100
// Be careful with these re-exports.

0 commit comments

Comments
 (0)