mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Report unresolved idents for implicit captures in format_args!()
And also a bit of cleanup by storing the capture's span with the open quote included.
This commit is contained in:
parent
27e824fad4
commit
54ce1dda3a
7 changed files with 110 additions and 63 deletions
|
@ -18,6 +18,7 @@ use smallvec::SmallVec;
|
||||||
use span::{Edition, MacroFileId};
|
use span::{Edition, MacroFileId};
|
||||||
use syntax::{ast, AstPtr, SyntaxNodePtr};
|
use syntax::{ast, AstPtr, SyntaxNodePtr};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
use tt::TextRange;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
|
@ -143,15 +144,7 @@ pub struct BodySourceMap {
|
||||||
|
|
||||||
pub types: TypesSourceMap,
|
pub types: TypesSourceMap,
|
||||||
|
|
||||||
// FIXME: Make this a sane struct.
|
template_map: Option<Box<FormatTemplate>>,
|
||||||
template_map: Option<
|
|
||||||
Box<(
|
|
||||||
// format_args!
|
|
||||||
FxHashMap<ExprId, (HygieneId, Vec<(syntax::TextRange, Name)>)>,
|
|
||||||
// asm!
|
|
||||||
FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
|
|
||||||
)>,
|
|
||||||
>,
|
|
||||||
|
|
||||||
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>,
|
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>,
|
||||||
|
|
||||||
|
@ -160,6 +153,20 @@ pub struct BodySourceMap {
|
||||||
diagnostics: Vec<BodyDiagnostic>,
|
diagnostics: Vec<BodyDiagnostic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Eq, PartialEq)]
|
||||||
|
struct FormatTemplate {
|
||||||
|
/// A map from `format_args!()` expressions to their captures.
|
||||||
|
format_args_to_captures: FxHashMap<ExprId, (HygieneId, Vec<(syntax::TextRange, Name)>)>,
|
||||||
|
/// A map from `asm!()` expressions to their captures.
|
||||||
|
asm_to_captures: FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
|
||||||
|
/// A map from desugared expressions of implicit captures to their source.
|
||||||
|
///
|
||||||
|
/// The value stored for each capture is its template literal and offset inside it. The template literal
|
||||||
|
/// is from the `format_args[_nl]!()` macro and so needs to be mapped up once to go to the user-written
|
||||||
|
/// template.
|
||||||
|
implicit_capture_to_source: FxHashMap<ExprId, InFile<(AstPtr<ast::Expr>, TextRange)>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum BodyDiagnostic {
|
pub enum BodyDiagnostic {
|
||||||
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
|
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
|
||||||
|
@ -798,18 +805,29 @@ impl BodySourceMap {
|
||||||
node: InFile<&ast::FormatArgsExpr>,
|
node: InFile<&ast::FormatArgsExpr>,
|
||||||
) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
|
) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
|
||||||
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
||||||
let (hygiene, names) =
|
let (hygiene, names) = self
|
||||||
self.template_map.as_ref()?.0.get(&self.expr_map.get(&src)?.as_expr()?)?;
|
.template_map
|
||||||
|
.as_ref()?
|
||||||
|
.format_args_to_captures
|
||||||
|
.get(&self.expr_map.get(&src)?.as_expr()?)?;
|
||||||
Some((*hygiene, &**names))
|
Some((*hygiene, &**names))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format_args_implicit_capture(
|
||||||
|
&self,
|
||||||
|
capture_expr: ExprId,
|
||||||
|
) -> Option<InFile<(AstPtr<ast::Expr>, TextRange)>> {
|
||||||
|
self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asm_template_args(
|
pub fn asm_template_args(
|
||||||
&self,
|
&self,
|
||||||
node: InFile<&ast::AsmExpr>,
|
node: InFile<&ast::AsmExpr>,
|
||||||
) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
|
) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
|
||||||
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
||||||
let expr = self.expr_map.get(&src)?.as_expr()?;
|
let expr = self.expr_map.get(&src)?.as_expr()?;
|
||||||
Some(expr).zip(self.template_map.as_ref()?.1.get(&expr).map(std::ops::Deref::deref))
|
Some(expr)
|
||||||
|
.zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the body source map's diagnostics.
|
/// Get a reference to the body source map's diagnostics.
|
||||||
|
@ -835,8 +853,14 @@ impl BodySourceMap {
|
||||||
types,
|
types,
|
||||||
} = self;
|
} = self;
|
||||||
if let Some(template_map) = template_map {
|
if let Some(template_map) = template_map {
|
||||||
template_map.0.shrink_to_fit();
|
let FormatTemplate {
|
||||||
template_map.1.shrink_to_fit();
|
format_args_to_captures,
|
||||||
|
asm_to_captures,
|
||||||
|
implicit_capture_to_source,
|
||||||
|
} = &mut **template_map;
|
||||||
|
format_args_to_captures.shrink_to_fit();
|
||||||
|
asm_to_captures.shrink_to_fit();
|
||||||
|
implicit_capture_to_source.shrink_to_fit();
|
||||||
}
|
}
|
||||||
expr_map.shrink_to_fit();
|
expr_map.shrink_to_fit();
|
||||||
expr_map_back.shrink_to_fit();
|
expr_map_back.shrink_to_fit();
|
||||||
|
|
|
@ -1957,8 +1957,10 @@ impl ExprCollector<'_> {
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
let mut mappings = vec![];
|
let mut mappings = vec![];
|
||||||
let (fmt, hygiene) = match template.and_then(|it| self.expand_macros_to_string(it)) {
|
let (fmt, hygiene) = match template.and_then(|template| {
|
||||||
Some((s, is_direct_literal)) => {
|
self.expand_macros_to_string(template.clone()).map(|it| (it, template))
|
||||||
|
}) {
|
||||||
|
Some(((s, is_direct_literal), template)) => {
|
||||||
let call_ctx = self.expander.syntax_context();
|
let call_ctx = self.expander.syntax_context();
|
||||||
let hygiene = self.hygiene_id_for(s.syntax().text_range().start());
|
let hygiene = self.hygiene_id_for(s.syntax().text_range().start());
|
||||||
let fmt = format_args::parse(
|
let fmt = format_args::parse(
|
||||||
|
@ -1966,8 +1968,18 @@ impl ExprCollector<'_> {
|
||||||
fmt_snippet,
|
fmt_snippet,
|
||||||
args,
|
args,
|
||||||
is_direct_literal,
|
is_direct_literal,
|
||||||
|name| {
|
|name, range| {
|
||||||
let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
|
let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
|
||||||
|
if let Some(range) = range {
|
||||||
|
self.source_map
|
||||||
|
.template_map
|
||||||
|
.get_or_insert_with(Default::default)
|
||||||
|
.implicit_capture_to_source
|
||||||
|
.insert(
|
||||||
|
expr_id,
|
||||||
|
self.expander.in_file((AstPtr::new(&template), range)),
|
||||||
|
);
|
||||||
|
}
|
||||||
if !hygiene.is_root() {
|
if !hygiene.is_root() {
|
||||||
self.body.expr_hygiene.insert(expr_id, hygiene);
|
self.body.expr_hygiene.insert(expr_id, hygiene);
|
||||||
}
|
}
|
||||||
|
@ -2139,7 +2151,7 @@ impl ExprCollector<'_> {
|
||||||
self.source_map
|
self.source_map
|
||||||
.template_map
|
.template_map
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
.0
|
.format_args_to_captures
|
||||||
.insert(idx, (hygiene, mappings));
|
.insert(idx, (hygiene, mappings));
|
||||||
idx
|
idx
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use syntax::{
|
||||||
ast::{self, HasName, IsString},
|
ast::{self, HasName, IsString},
|
||||||
AstNode, AstPtr, AstToken, T,
|
AstNode, AstPtr, AstToken, T,
|
||||||
};
|
};
|
||||||
use tt::{TextRange, TextSize};
|
use tt::TextRange;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::lower::{ExprCollector, FxIndexSet},
|
body::lower::{ExprCollector, FxIndexSet},
|
||||||
|
@ -224,7 +224,7 @@ impl ExprCollector<'_> {
|
||||||
TextRange::new(
|
TextRange::new(
|
||||||
inner_span.start.try_into().unwrap(),
|
inner_span.start.try_into().unwrap(),
|
||||||
inner_span.end.try_into().unwrap(),
|
inner_span.end.try_into().unwrap(),
|
||||||
) - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1)
|
)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
for piece in unverified_pieces {
|
for piece in unverified_pieces {
|
||||||
|
@ -268,7 +268,11 @@ impl ExprCollector<'_> {
|
||||||
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
|
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
|
||||||
syntax_ptr,
|
syntax_ptr,
|
||||||
);
|
);
|
||||||
self.source_map.template_map.get_or_insert_with(Default::default).1.insert(idx, mappings);
|
self.source_map
|
||||||
|
.template_map
|
||||||
|
.get_or_insert_with(Default::default)
|
||||||
|
.asm_to_captures
|
||||||
|
.insert(idx, mappings);
|
||||||
idx
|
idx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Parses `format_args` input.
|
//! Parses `format_args` input.
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use intern::Symbol;
|
use intern::Symbol;
|
||||||
use rustc_parse_format as parse;
|
use rustc_parse_format as parse;
|
||||||
|
@ -7,7 +8,7 @@ use span::SyntaxContextId;
|
||||||
use stdx::TupleExt;
|
use stdx::TupleExt;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, IsString},
|
ast::{self, IsString},
|
||||||
TextRange, TextSize,
|
TextRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::hir::ExprId;
|
use crate::hir::ExprId;
|
||||||
|
@ -33,7 +34,7 @@ pub enum FormatArgsPiece {
|
||||||
Placeholder(FormatPlaceholder),
|
Placeholder(FormatPlaceholder),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct FormatPlaceholder {
|
pub struct FormatPlaceholder {
|
||||||
/// Index into [`FormatArgs::arguments`].
|
/// Index into [`FormatArgs::arguments`].
|
||||||
pub argument: FormatArgPosition,
|
pub argument: FormatArgPosition,
|
||||||
|
@ -45,11 +46,11 @@ pub struct FormatPlaceholder {
|
||||||
pub format_options: FormatOptions,
|
pub format_options: FormatOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct FormatArgPosition {
|
pub struct FormatArgPosition {
|
||||||
/// Which argument this position refers to (Ok),
|
/// Which argument this position refers to (Ok),
|
||||||
/// or would've referred to if it existed (Err).
|
/// or would've referred to if it existed (Err).
|
||||||
pub index: Result<usize, usize>,
|
pub index: Result<usize, Either<usize, Name>>,
|
||||||
/// What kind of position this is. See [`FormatArgPositionKind`].
|
/// What kind of position this is. See [`FormatArgPositionKind`].
|
||||||
pub kind: FormatArgPositionKind,
|
pub kind: FormatArgPositionKind,
|
||||||
/// The span of the name or number.
|
/// The span of the name or number.
|
||||||
|
@ -88,7 +89,7 @@ pub enum FormatTrait {
|
||||||
UpperHex,
|
UpperHex,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
|
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
||||||
pub struct FormatOptions {
|
pub struct FormatOptions {
|
||||||
/// The width. E.g. `{:5}` or `{:width$}`.
|
/// The width. E.g. `{:5}` or `{:width$}`.
|
||||||
pub width: Option<FormatCount>,
|
pub width: Option<FormatCount>,
|
||||||
|
@ -133,7 +134,7 @@ pub enum FormatAlignment {
|
||||||
Center,
|
Center,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum FormatCount {
|
pub enum FormatCount {
|
||||||
/// `{:5}` or `{:.5}`
|
/// `{:5}` or `{:.5}`
|
||||||
Literal(usize),
|
Literal(usize),
|
||||||
|
@ -173,7 +174,7 @@ pub(crate) fn parse(
|
||||||
fmt_snippet: Option<String>,
|
fmt_snippet: Option<String>,
|
||||||
mut args: FormatArgumentsCollector,
|
mut args: FormatArgumentsCollector,
|
||||||
is_direct_literal: bool,
|
is_direct_literal: bool,
|
||||||
mut synth: impl FnMut(Name) -> ExprId,
|
mut synth: impl FnMut(Name, Option<TextRange>) -> ExprId,
|
||||||
mut record_usage: impl FnMut(Name, Option<TextRange>),
|
mut record_usage: impl FnMut(Name, Option<TextRange>),
|
||||||
call_ctx: SyntaxContextId,
|
call_ctx: SyntaxContextId,
|
||||||
) -> FormatArgs {
|
) -> FormatArgs {
|
||||||
|
@ -192,7 +193,6 @@ pub(crate) fn parse(
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut parser =
|
let mut parser =
|
||||||
parse::Parser::new(&text, str_style, fmt_snippet, false, parse::ParseMode::Format);
|
parse::Parser::new(&text, str_style, fmt_snippet, false, parse::ParseMode::Format);
|
||||||
|
|
||||||
|
@ -217,7 +217,6 @@ pub(crate) fn parse(
|
||||||
let to_span = |inner_span: parse::InnerSpan| {
|
let to_span = |inner_span: parse::InnerSpan| {
|
||||||
is_source_literal.then(|| {
|
is_source_literal.then(|| {
|
||||||
TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
|
TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
|
||||||
- TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1)
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -245,8 +244,8 @@ pub(crate) fn parse(
|
||||||
Ok(index)
|
Ok(index)
|
||||||
} else {
|
} else {
|
||||||
// Doesn't exist as an explicit argument.
|
// Doesn't exist as an explicit argument.
|
||||||
invalid_refs.push((index, span, used_as, kind));
|
invalid_refs.push((Either::Left(index), span, used_as, kind));
|
||||||
Err(index)
|
Err(Either::Left(index))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ArgRef::Name(name, span) => {
|
ArgRef::Name(name, span) => {
|
||||||
|
@ -265,14 +264,17 @@ pub(crate) fn parse(
|
||||||
// For the moment capturing variables from format strings expanded from macros is
|
// For the moment capturing variables from format strings expanded from macros is
|
||||||
// disabled (see RFC #2795)
|
// disabled (see RFC #2795)
|
||||||
// FIXME: Diagnose
|
// FIXME: Diagnose
|
||||||
|
invalid_refs.push((Either::Right(name.clone()), span, used_as, kind));
|
||||||
|
Err(Either::Right(name))
|
||||||
|
} else {
|
||||||
|
record_usage(name.clone(), span);
|
||||||
|
Ok(args.add(FormatArgument {
|
||||||
|
kind: FormatArgumentKind::Captured(name.clone()),
|
||||||
|
// FIXME: This is problematic, we might want to synthesize a dummy
|
||||||
|
// expression proper and/or desugar these.
|
||||||
|
expr: synth(name, span),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
record_usage(name.clone(), span);
|
|
||||||
Ok(args.add(FormatArgument {
|
|
||||||
kind: FormatArgumentKind::Captured(name.clone()),
|
|
||||||
// FIXME: This is problematic, we might want to synthesize a dummy
|
|
||||||
// expression proper and/or desugar these.
|
|
||||||
expr: synth(name),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -262,7 +262,7 @@ pub struct UnresolvedAssocItem {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UnresolvedIdent {
|
pub struct UnresolvedIdent {
|
||||||
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
|
pub node: InFile<(AstPtr<Either<ast::Expr, ast::Pat>>, Option<TextRange>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -550,11 +550,10 @@ impl AnyDiagnostic {
|
||||||
source_map: &hir_def::body::BodySourceMap,
|
source_map: &hir_def::body::BodySourceMap,
|
||||||
) -> Option<AnyDiagnostic> {
|
) -> Option<AnyDiagnostic> {
|
||||||
let expr_syntax = |expr| {
|
let expr_syntax = |expr| {
|
||||||
source_map.expr_syntax(expr).inspect_err(|_| tracing::error!("synthetic syntax")).ok()
|
source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok()
|
||||||
};
|
|
||||||
let pat_syntax = |pat| {
|
|
||||||
source_map.pat_syntax(pat).inspect_err(|_| tracing::error!("synthetic syntax")).ok()
|
|
||||||
};
|
};
|
||||||
|
let pat_syntax =
|
||||||
|
|pat| source_map.pat_syntax(pat).inspect_err(|_| stdx::never!("synthetic syntax")).ok();
|
||||||
let expr_or_pat_syntax = |id| match id {
|
let expr_or_pat_syntax = |id| match id {
|
||||||
ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(|it| it.map(AstPtr::wrap_left)),
|
ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(|it| it.map(AstPtr::wrap_left)),
|
||||||
ExprOrPatId::PatId(pat) => pat_syntax(pat),
|
ExprOrPatId::PatId(pat) => pat_syntax(pat),
|
||||||
|
@ -626,8 +625,16 @@ impl AnyDiagnostic {
|
||||||
UnresolvedAssocItem { expr_or_pat }.into()
|
UnresolvedAssocItem { expr_or_pat }.into()
|
||||||
}
|
}
|
||||||
&InferenceDiagnostic::UnresolvedIdent { id } => {
|
&InferenceDiagnostic::UnresolvedIdent { id } => {
|
||||||
let expr_or_pat = expr_or_pat_syntax(id)?;
|
let node = match id {
|
||||||
UnresolvedIdent { expr_or_pat }.into()
|
ExprOrPatId::ExprId(id) => match source_map.expr_syntax(id) {
|
||||||
|
Ok(syntax) => syntax.map(|it| (it.wrap_left(), None)),
|
||||||
|
Err(SyntheticSyntax) => source_map
|
||||||
|
.format_args_implicit_capture(id)?
|
||||||
|
.map(|(node, range)| (node.wrap_left(), Some(range))),
|
||||||
|
},
|
||||||
|
ExprOrPatId::PatId(id) => pat_syntax(id)?.map(|it| (it, None)),
|
||||||
|
};
|
||||||
|
UnresolvedIdent { node }.into()
|
||||||
}
|
}
|
||||||
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
|
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
|
||||||
let expr = expr_syntax(expr)?;
|
let expr = expr_syntax(expr)?;
|
||||||
|
|
|
@ -38,7 +38,7 @@ use span::{AstIdMap, EditionedFileId, FileId, HirFileIdRepr, SyntaxContextId};
|
||||||
use stdx::TupleExt;
|
use stdx::TupleExt;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::skip_trivia_token,
|
algo::skip_trivia_token,
|
||||||
ast::{self, HasAttrs as _, HasGenericParams, IsString as _},
|
ast::{self, HasAttrs as _, HasGenericParams},
|
||||||
AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
|
AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
|
||||||
TextSize,
|
TextSize,
|
||||||
};
|
};
|
||||||
|
@ -643,8 +643,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
&self,
|
&self,
|
||||||
string: &ast::String,
|
string: &ast::String,
|
||||||
) -> Option<Vec<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)>> {
|
) -> Option<Vec<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)>> {
|
||||||
let quote = string.open_quote_text_range()?;
|
let string_start = string.syntax().text_range().start();
|
||||||
|
|
||||||
let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
|
let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
|
||||||
self.descend_into_macros_breakable(token, |token, _| {
|
self.descend_into_macros_breakable(token, |token, _| {
|
||||||
(|| {
|
(|| {
|
||||||
|
@ -658,7 +657,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
let format_args = self.wrap_node_infile(format_args);
|
let format_args = self.wrap_node_infile(format_args);
|
||||||
let res = source_analyzer
|
let res = source_analyzer
|
||||||
.as_format_args_parts(self.db, format_args.as_ref())?
|
.as_format_args_parts(self.db, format_args.as_ref())?
|
||||||
.map(|(range, res)| (range + quote.end(), res.map(Either::Left)))
|
.map(|(range, res)| (range + string_start, res.map(Either::Left)))
|
||||||
.collect();
|
.collect();
|
||||||
Some(res)
|
Some(res)
|
||||||
} else {
|
} else {
|
||||||
|
@ -672,7 +671,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&(range, index)| {
|
.map(|&(range, index)| {
|
||||||
(
|
(
|
||||||
range + quote.end(),
|
range + string_start,
|
||||||
Some(Either::Right(InlineAsmOperand { owner, expr, index })),
|
Some(Either::Right(InlineAsmOperand { owner, expr, index })),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -690,17 +689,16 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
original_token: SyntaxToken,
|
original_token: SyntaxToken,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> {
|
) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> {
|
||||||
let original_string = ast::String::cast(original_token.clone())?;
|
let string_start = original_token.text_range().start();
|
||||||
let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
|
let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
|
||||||
let quote = original_string.open_quote_text_range()?;
|
|
||||||
self.descend_into_macros_breakable(original_token, |token, _| {
|
self.descend_into_macros_breakable(original_token, |token, _| {
|
||||||
(|| {
|
(|| {
|
||||||
let token = token.value;
|
let token = token.value;
|
||||||
self.resolve_offset_in_format_args(
|
self.resolve_offset_in_format_args(
|
||||||
ast::String::cast(token)?,
|
ast::String::cast(token)?,
|
||||||
offset.checked_sub(quote.end())?,
|
offset.checked_sub(string_start)?,
|
||||||
)
|
)
|
||||||
.map(|(range, res)| (range + quote.end(), res))
|
.map(|(range, res)| (range + string_start, res))
|
||||||
})()
|
})()
|
||||||
.map_or(ControlFlow::Continue(()), ControlFlow::Break)
|
.map_or(ControlFlow::Continue(()), ControlFlow::Break)
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,20 +7,19 @@ pub(crate) fn unresolved_ident(
|
||||||
ctx: &DiagnosticsContext<'_>,
|
ctx: &DiagnosticsContext<'_>,
|
||||||
d: &hir::UnresolvedIdent,
|
d: &hir::UnresolvedIdent,
|
||||||
) -> Diagnostic {
|
) -> Diagnostic {
|
||||||
Diagnostic::new_with_syntax_node_ptr(
|
let mut range =
|
||||||
ctx,
|
ctx.sema.diagnostics_display_range(d.node.map(|(node, _)| node.syntax_node_ptr()));
|
||||||
DiagnosticCode::RustcHardError("E0425"),
|
if let Some(in_node_range) = d.node.value.1 {
|
||||||
"no such value in this scope",
|
range.range = in_node_range + range.range.start();
|
||||||
d.expr_or_pat.map(Into::into),
|
}
|
||||||
)
|
Diagnostic::new(DiagnosticCode::RustcHardError("E0425"), "no such value in this scope", range)
|
||||||
.experimental()
|
.experimental()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::check_diagnostics;
|
use crate::tests::check_diagnostics;
|
||||||
|
|
||||||
// FIXME: This should show a diagnostic
|
|
||||||
#[test]
|
#[test]
|
||||||
fn feature() {
|
fn feature() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
|
@ -28,6 +27,7 @@ mod tests {
|
||||||
//- minicore: fmt
|
//- minicore: fmt
|
||||||
fn main() {
|
fn main() {
|
||||||
format_args!("{unresolved}");
|
format_args!("{unresolved}");
|
||||||
|
// ^^^^^^^^^^ error: no such value in this scope
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue