mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
Merge branch 'master' into sync-from-rust
This commit is contained in:
commit
d39b45a58d
195 changed files with 5778 additions and 2751 deletions
|
@ -5,7 +5,7 @@ pub mod builtin;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{hash::Hash, ops};
|
||||
use std::{hash::Hash, ops, slice::Iter as SliceIter};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
|
@ -14,12 +14,11 @@ use hir_expand::{
|
|||
attrs::{collect_attrs, Attr, AttrId, RawAttrs},
|
||||
HirFileId, InFile,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use la_arena::{ArenaMap, Idx, RawIdx};
|
||||
use mbe::DelimiterKind;
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs, IsString},
|
||||
AstPtr, AstToken, SmolStr, TextRange, TextSize,
|
||||
ast::{self, HasAttrs},
|
||||
AstPtr, SmolStr,
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
|
@ -33,26 +32,6 @@ use crate::{
|
|||
LocalFieldId, Lookup, MacroId, VariantId,
|
||||
};
|
||||
|
||||
/// Holds documentation
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Documentation(String);
|
||||
|
||||
impl Documentation {
|
||||
pub fn new(s: String) -> Self {
|
||||
Documentation(s)
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Documentation> for String {
|
||||
fn from(Documentation(string): Documentation) -> Self {
|
||||
string
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Attrs(RawAttrs);
|
||||
|
||||
|
@ -221,33 +200,6 @@ impl Attrs {
|
|||
self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
|
||||
}
|
||||
|
||||
pub fn docs(&self) -> Option<Documentation> {
|
||||
let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
|
||||
let indent = doc_indent(self);
|
||||
let mut buf = String::new();
|
||||
for doc in docs {
|
||||
// str::lines doesn't yield anything for the empty string
|
||||
if !doc.is_empty() {
|
||||
buf.extend(Itertools::intersperse(
|
||||
doc.lines().map(|line| {
|
||||
line.char_indices()
|
||||
.nth(indent)
|
||||
.map_or(line, |(offset, _)| &line[offset..])
|
||||
.trim_end()
|
||||
}),
|
||||
"\n",
|
||||
));
|
||||
}
|
||||
buf.push('\n');
|
||||
}
|
||||
buf.pop();
|
||||
if buf.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Documentation(buf))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_doc_hidden(&self) -> bool {
|
||||
self.by_key("doc").tt_values().any(|tt| {
|
||||
tt.delimiter.kind == DelimiterKind::Parenthesis &&
|
||||
|
@ -299,7 +251,6 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
|
||||
use std::slice::Iter as SliceIter;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub enum DocAtom {
|
||||
/// eg. `#[doc(hidden)]`
|
||||
|
@ -313,7 +264,6 @@ pub enum DocAtom {
|
|||
|
||||
// Adapted from `CfgExpr` parsing code
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
|
||||
pub enum DocExpr {
|
||||
Invalid,
|
||||
/// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
|
||||
|
@ -574,62 +524,6 @@ impl AttrsWithOwner {
|
|||
|
||||
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
|
||||
}
|
||||
|
||||
pub fn docs_with_rangemap(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
) -> Option<(Documentation, DocsRangeMap)> {
|
||||
let docs =
|
||||
self.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id)));
|
||||
let indent = doc_indent(self);
|
||||
let mut buf = String::new();
|
||||
let mut mapping = Vec::new();
|
||||
for (doc, idx) in docs {
|
||||
if !doc.is_empty() {
|
||||
let mut base_offset = 0;
|
||||
for raw_line in doc.split('\n') {
|
||||
let line = raw_line.trim_end();
|
||||
let line_len = line.len();
|
||||
let (offset, line) = match line.char_indices().nth(indent) {
|
||||
Some((offset, _)) => (offset, &line[offset..]),
|
||||
None => (0, line),
|
||||
};
|
||||
let buf_offset = buf.len();
|
||||
buf.push_str(line);
|
||||
mapping.push((
|
||||
TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?),
|
||||
idx,
|
||||
TextRange::at(
|
||||
(base_offset + offset).try_into().ok()?,
|
||||
line_len.try_into().ok()?,
|
||||
),
|
||||
));
|
||||
buf.push('\n');
|
||||
base_offset += raw_line.len() + 1;
|
||||
}
|
||||
} else {
|
||||
buf.push('\n');
|
||||
}
|
||||
}
|
||||
buf.pop();
|
||||
if buf.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_indent(attrs: &Attrs) -> usize {
|
||||
attrs
|
||||
.by_key("doc")
|
||||
.attrs()
|
||||
.filter_map(|attr| attr.string_value())
|
||||
.flat_map(|s| s.lines())
|
||||
.filter(|line| !line.chars().all(|c| c.is_whitespace()))
|
||||
.map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
|
||||
.min()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -673,7 +567,7 @@ impl AttrSourceMap {
|
|||
self.source_of_id(attr.id)
|
||||
}
|
||||
|
||||
fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
|
||||
pub fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
|
||||
let ast_idx = id.ast_index();
|
||||
let file_id = match self.mod_def_site_file_id {
|
||||
Some((file_id, def_site_cut)) if def_site_cut <= ast_idx => file_id,
|
||||
|
@ -687,69 +581,6 @@ impl AttrSourceMap {
|
|||
}
|
||||
}
|
||||
|
||||
/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
|
||||
#[derive(Debug)]
|
||||
pub struct DocsRangeMap {
|
||||
source_map: AttrSourceMap,
|
||||
// (docstring-line-range, attr_index, attr-string-range)
|
||||
// a mapping from the text range of a line of the [`Documentation`] to the attribute index and
|
||||
// the original (untrimmed) syntax doc line
|
||||
mapping: Vec<(TextRange, AttrId, TextRange)>,
|
||||
}
|
||||
|
||||
impl DocsRangeMap {
|
||||
/// Maps a [`TextRange`] relative to the documentation string back to its AST range
|
||||
pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
|
||||
let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
|
||||
let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
|
||||
if !line_docs_range.contains_range(range) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let relative_range = range - line_docs_range.start();
|
||||
|
||||
let InFile { file_id, value: source } = self.source_map.source_of_id(idx);
|
||||
match source {
|
||||
Either::Left(attr) => {
|
||||
let string = get_doc_string_in_attr(attr)?;
|
||||
let text_range = string.open_quote_text_range()?;
|
||||
let range = TextRange::at(
|
||||
text_range.end() + original_line_src_range.start() + relative_range.start(),
|
||||
string.syntax().text_range().len().min(range.len()),
|
||||
);
|
||||
Some(InFile { file_id, value: range })
|
||||
}
|
||||
Either::Right(comment) => {
|
||||
let text_range = comment.syntax().text_range();
|
||||
let range = TextRange::at(
|
||||
text_range.start()
|
||||
+ TextSize::try_from(comment.prefix().len()).ok()?
|
||||
+ original_line_src_range.start()
|
||||
+ relative_range.start(),
|
||||
text_range.len().min(range.len()),
|
||||
);
|
||||
Some(InFile { file_id, value: range })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
|
||||
match it.expr() {
|
||||
// #[doc = lit]
|
||||
Some(ast::Expr::Literal(lit)) => match lit.kind() {
|
||||
ast::LiteralKind::String(it) => Some(it),
|
||||
_ => None,
|
||||
},
|
||||
// #[cfg_attr(..., doc = "", ...)]
|
||||
None => {
|
||||
// FIXME: See highlight injection for what to do here
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AttrQuery<'attr> {
|
||||
attrs: &'attr Attrs,
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
|
||||
//! ease updating.
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
/// Ignored attribute namespaces used by tools.
|
||||
|
@ -29,7 +30,7 @@ pub struct AttributeTemplate {
|
|||
}
|
||||
|
||||
pub fn find_builtin_attr_idx(name: &str) -> Option<usize> {
|
||||
static BUILTIN_LOOKUP_TABLE: OnceCell<FxHashMap<&'static str, usize>> = OnceCell::new();
|
||||
static BUILTIN_LOOKUP_TABLE: OnceLock<FxHashMap<&'static str, usize>> = OnceLock::new();
|
||||
BUILTIN_LOOKUP_TABLE
|
||||
.get_or_init(|| {
|
||||
INERT_ATTRIBUTES.iter().map(|attr| attr.name).enumerate().map(|(a, b)| (b, a)).collect()
|
||||
|
|
|
@ -65,6 +65,8 @@ pub type LabelSource = InFile<LabelPtr>;
|
|||
|
||||
pub type FieldPtr = AstPtr<ast::RecordExprField>;
|
||||
pub type FieldSource = InFile<FieldPtr>;
|
||||
pub type PatFieldPtr = AstPtr<ast::RecordPatField>;
|
||||
pub type PatFieldSource = InFile<PatFieldPtr>;
|
||||
|
||||
/// An item body together with the mapping from syntax nodes to HIR expression
|
||||
/// IDs. This is needed to go from e.g. a position in a file to the HIR
|
||||
|
@ -90,8 +92,8 @@ pub struct BodySourceMap {
|
|||
|
||||
/// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
|
||||
/// Instead, we use id of expression (`92`) to identify the field.
|
||||
field_map: FxHashMap<FieldSource, ExprId>,
|
||||
field_map_back: FxHashMap<ExprId, FieldSource>,
|
||||
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
|
||||
|
||||
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
|
||||
|
||||
|
@ -164,9 +166,10 @@ impl Body {
|
|||
};
|
||||
let module = def.module(db);
|
||||
let expander = Expander::new(db, file_id, module);
|
||||
let (mut body, source_map) =
|
||||
let (mut body, mut source_map) =
|
||||
Body::new(db, def, expander, params, body, module.krate, is_async_fn);
|
||||
body.shrink_to_fit();
|
||||
source_map.shrink_to_fit();
|
||||
|
||||
(Arc::new(body), Arc::new(source_map))
|
||||
}
|
||||
|
@ -375,9 +378,8 @@ impl BodySourceMap {
|
|||
self.field_map_back[&expr].clone()
|
||||
}
|
||||
|
||||
pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId> {
|
||||
let src = node.map(AstPtr::new);
|
||||
self.field_map.get(&src).cloned()
|
||||
pub fn pat_field_syntax(&self, pat: PatId) -> PatFieldSource {
|
||||
self.pat_field_map_back[&pat].clone()
|
||||
}
|
||||
|
||||
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId> {
|
||||
|
@ -389,4 +391,29 @@ impl BodySourceMap {
|
|||
pub fn diagnostics(&self) -> &[BodyDiagnostic] {
|
||||
&self.diagnostics
|
||||
}
|
||||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
let Self {
|
||||
expr_map,
|
||||
expr_map_back,
|
||||
pat_map,
|
||||
pat_map_back,
|
||||
label_map,
|
||||
label_map_back,
|
||||
field_map_back,
|
||||
pat_field_map_back,
|
||||
expansions,
|
||||
diagnostics,
|
||||
} = self;
|
||||
expr_map.shrink_to_fit();
|
||||
expr_map_back.shrink_to_fit();
|
||||
pat_map.shrink_to_fit();
|
||||
pat_map_back.shrink_to_fit();
|
||||
label_map.shrink_to_fit();
|
||||
label_map_back.shrink_to_fit();
|
||||
field_map_back.shrink_to_fit();
|
||||
pat_field_map_back.shrink_to_fit();
|
||||
expansions.shrink_to_fit();
|
||||
diagnostics.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,13 +25,20 @@ use triomphe::Arc;
|
|||
|
||||
use crate::{
|
||||
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
|
||||
builtin_type::BuiltinUint,
|
||||
data::adt::StructKind,
|
||||
db::DefDatabase,
|
||||
expander::Expander,
|
||||
hir::{
|
||||
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy,
|
||||
ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
|
||||
Pat, PatId, RecordFieldPat, RecordLitField, Statement,
|
||||
dummy_expr_id,
|
||||
format_args::{
|
||||
self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
|
||||
FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
|
||||
FormatPlaceholder, FormatSign, FormatTrait,
|
||||
},
|
||||
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
|
||||
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
|
||||
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
|
||||
},
|
||||
item_scope::BuiltinShadowMode,
|
||||
lang_item::LangItem,
|
||||
|
@ -42,6 +49,8 @@ use crate::{
|
|||
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
|
||||
};
|
||||
|
||||
type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
|
||||
|
||||
pub(super) fn lower(
|
||||
db: &dyn DefDatabase,
|
||||
owner: DefWithBodyId,
|
||||
|
@ -437,7 +446,6 @@ impl ExprCollector<'_> {
|
|||
None => self.missing_expr(),
|
||||
};
|
||||
let src = self.expander.to_source(AstPtr::new(&field));
|
||||
self.source_map.field_map.insert(src.clone(), expr);
|
||||
self.source_map.field_map_back.insert(expr, src);
|
||||
Some(RecordLitField { name, expr })
|
||||
})
|
||||
|
@ -579,11 +587,6 @@ impl ExprCollector<'_> {
|
|||
syntax_ptr,
|
||||
)
|
||||
}
|
||||
ast::Expr::BoxExpr(e) => {
|
||||
let expr = self.collect_expr_opt(e.expr());
|
||||
self.alloc_expr(Expr::Box { expr }, syntax_ptr)
|
||||
}
|
||||
|
||||
ast::Expr::ArrayExpr(e) => {
|
||||
let kind = e.kind();
|
||||
|
||||
|
@ -653,6 +656,16 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
}
|
||||
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
|
||||
ast::Expr::AsmExpr(e) => {
|
||||
let e = self.collect_expr_opt(e.expr());
|
||||
self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
|
||||
}
|
||||
ast::Expr::OffsetOfExpr(e) => {
|
||||
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
|
||||
let fields = e.fields().map(|it| it.as_name()).collect();
|
||||
self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr)
|
||||
}
|
||||
ast::Expr::FormatArgsExpr(f) => self.collect_format_args(f, syntax_ptr),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -663,6 +676,7 @@ impl ExprCollector<'_> {
|
|||
let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
|
||||
let prev_binding_owner = self.current_binding_owner.take();
|
||||
self.current_binding_owner = Some(result_expr_id);
|
||||
|
||||
(result_expr_id, prev_binding_owner)
|
||||
}
|
||||
|
||||
|
@ -744,7 +758,27 @@ impl ExprCollector<'_> {
|
|||
fn collect_while_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::WhileExpr) -> ExprId {
|
||||
let label = e.label().map(|label| self.collect_label(label));
|
||||
let body = self.collect_labelled_block_opt(label, e.loop_body());
|
||||
let condition = self.collect_expr_opt(e.condition());
|
||||
|
||||
// Labels can also be used in the condition expression, like this:
|
||||
// ```
|
||||
// fn main() {
|
||||
// let mut optional = Some(0);
|
||||
// 'my_label: while let Some(a) = match optional {
|
||||
// None => break 'my_label,
|
||||
// Some(val) => Some(val),
|
||||
// } {
|
||||
// println!("{}", a);
|
||||
// optional = None;
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
let condition = match label {
|
||||
Some(label) => {
|
||||
self.with_labeled_rib(label, |this| this.collect_expr_opt(e.condition()))
|
||||
}
|
||||
None => self.collect_expr_opt(e.condition()),
|
||||
};
|
||||
|
||||
let break_expr =
|
||||
self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone());
|
||||
let if_expr = self.alloc_expr(
|
||||
|
@ -1295,23 +1329,21 @@ impl ExprCollector<'_> {
|
|||
ast::Pat::RecordPat(p) => {
|
||||
let path =
|
||||
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
||||
let args = p
|
||||
.record_pat_field_list()
|
||||
.expect("every struct should have a field list")
|
||||
let record_pat_field_list =
|
||||
&p.record_pat_field_list().expect("every struct should have a field list");
|
||||
let args = record_pat_field_list
|
||||
.fields()
|
||||
.filter_map(|f| {
|
||||
let ast_pat = f.pat()?;
|
||||
let pat = self.collect_pat(ast_pat, binding_list);
|
||||
let name = f.field_name()?.as_name();
|
||||
let src = self.expander.to_source(AstPtr::new(&f));
|
||||
self.source_map.pat_field_map_back.insert(pat, src);
|
||||
Some(RecordFieldPat { name, pat })
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ellipsis = p
|
||||
.record_pat_field_list()
|
||||
.expect("every struct should have a field list")
|
||||
.rest_pat()
|
||||
.is_some();
|
||||
let ellipsis = record_pat_field_list.rest_pat().is_some();
|
||||
|
||||
Pat::Record { path, args, ellipsis }
|
||||
}
|
||||
|
@ -1531,6 +1563,401 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
}
|
||||
// endregion: labels
|
||||
|
||||
// region: format
|
||||
fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> {
|
||||
let m = match expr {
|
||||
ast::Expr::MacroExpr(m) => m,
|
||||
ast::Expr::Literal(l) => {
|
||||
return match l.kind() {
|
||||
ast::LiteralKind::String(s) => Some((s, true)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let e = m.macro_call()?;
|
||||
let macro_ptr = AstPtr::new(&e);
|
||||
let (exp, _) = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
|
||||
expansion.and_then(|it| this.expand_macros_to_string(it))
|
||||
})?;
|
||||
Some((exp, false))
|
||||
}
|
||||
|
||||
fn collect_format_args(
|
||||
&mut self,
|
||||
f: ast::FormatArgsExpr,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
) -> ExprId {
|
||||
let mut args = FormatArgumentsCollector::new();
|
||||
f.args().for_each(|arg| {
|
||||
args.add(FormatArgument {
|
||||
kind: match arg.name() {
|
||||
Some(name) => FormatArgumentKind::Named(name.as_name()),
|
||||
None => FormatArgumentKind::Normal,
|
||||
},
|
||||
expr: self.collect_expr_opt(arg.expr()),
|
||||
});
|
||||
});
|
||||
let template = f.template();
|
||||
let fmt_snippet = template.as_ref().map(ToString::to_string);
|
||||
let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) {
|
||||
Some((s, is_direct_literal)) => {
|
||||
format_args::parse(&s, fmt_snippet, args, is_direct_literal, |name| {
|
||||
self.alloc_expr_desugared(Expr::Path(Path::from(name)))
|
||||
})
|
||||
}
|
||||
None => FormatArgs { template: Default::default(), arguments: args.finish() },
|
||||
};
|
||||
|
||||
// Create a list of all _unique_ (argument, format trait) combinations.
|
||||
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
|
||||
let mut argmap = FxIndexSet::default();
|
||||
for piece in fmt.template.iter() {
|
||||
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
|
||||
if let Ok(index) = placeholder.argument.index {
|
||||
argmap.insert((index, ArgumentType::Format(placeholder.format_trait)));
|
||||
}
|
||||
}
|
||||
|
||||
let lit_pieces =
|
||||
fmt.template
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, piece)| {
|
||||
match piece {
|
||||
FormatArgsPiece::Literal(s) => Some(
|
||||
self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))),
|
||||
),
|
||||
&FormatArgsPiece::Placeholder(_) => {
|
||||
// Inject empty string before placeholders when not already preceded by a literal piece.
|
||||
if i == 0
|
||||
|| matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_))
|
||||
{
|
||||
Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(
|
||||
"".into(),
|
||||
))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: lit_pieces,
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
let lit_pieces = self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: lit_pieces,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
});
|
||||
let format_options = {
|
||||
// Generate:
|
||||
// &[format_spec_0, format_spec_1, format_spec_2]
|
||||
let elements = fmt
|
||||
.template
|
||||
.iter()
|
||||
.filter_map(|piece| {
|
||||
let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
|
||||
Some(self.make_format_spec(placeholder, &mut argmap))
|
||||
})
|
||||
.collect();
|
||||
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements,
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: array,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
})
|
||||
};
|
||||
let arguments = &*fmt.arguments.arguments;
|
||||
|
||||
let args = if arguments.is_empty() {
|
||||
let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: Box::default(),
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
})
|
||||
} else {
|
||||
// Generate:
|
||||
// &match (&arg0, &arg1, &…) {
|
||||
// args => [
|
||||
// <core::fmt::Argument>::new_display(args.0),
|
||||
// <core::fmt::Argument>::new_lower_hex(args.1),
|
||||
// <core::fmt::Argument>::new_debug(args.0),
|
||||
// …
|
||||
// ]
|
||||
// }
|
||||
let args = argmap
|
||||
.iter()
|
||||
.map(|&(arg_index, ty)| {
|
||||
let arg = self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: arguments[arg_index].expr,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
});
|
||||
self.make_argument(arg, ty)
|
||||
})
|
||||
.collect();
|
||||
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: args,
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: array,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
})
|
||||
};
|
||||
|
||||
// Generate:
|
||||
// <core::fmt::Arguments>::new_v1_formatted(
|
||||
// lit_pieces,
|
||||
// args,
|
||||
// format_options,
|
||||
// unsafe { ::core::fmt::UnsafeArg::new() }
|
||||
// )
|
||||
|
||||
let Some(new_v1_formatted) =
|
||||
LangItem::FormatArguments.ty_rel_path(self.db, self.krate, name![new_v1_formatted])
|
||||
else {
|
||||
return self.missing_expr();
|
||||
};
|
||||
let Some(unsafe_arg_new) =
|
||||
LangItem::FormatUnsafeArg.ty_rel_path(self.db, self.krate, name![new])
|
||||
else {
|
||||
return self.missing_expr();
|
||||
};
|
||||
let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted));
|
||||
|
||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
|
||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call {
|
||||
callee: unsafe_arg_new,
|
||||
args: Box::default(),
|
||||
is_assignee_expr: false,
|
||||
});
|
||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
|
||||
id: None,
|
||||
statements: Box::default(),
|
||||
tail: Some(unsafe_arg_new),
|
||||
});
|
||||
|
||||
self.alloc_expr(
|
||||
Expr::Call {
|
||||
callee: new_v1_formatted,
|
||||
args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
|
||||
is_assignee_expr: false,
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate a hir expression for a format_args placeholder specification.
|
||||
///
|
||||
/// Generates
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::rt::Placeholder::new(
|
||||
/// …usize, // position
|
||||
/// '…', // fill
|
||||
/// <core::fmt::rt::Alignment>::…, // alignment
|
||||
/// …u32, // flags
|
||||
/// <core::fmt::rt::Count::…>, // width
|
||||
/// <core::fmt::rt::Count::…>, // precision
|
||||
/// )
|
||||
/// ```
|
||||
fn make_format_spec(
|
||||
&mut self,
|
||||
placeholder: &FormatPlaceholder,
|
||||
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
||||
) -> ExprId {
|
||||
let position = match placeholder.argument.index {
|
||||
Ok(arg_index) => {
|
||||
let (i, _) =
|
||||
argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
|
||||
self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
|
||||
i as u128,
|
||||
Some(BuiltinUint::Usize),
|
||||
)))
|
||||
}
|
||||
Err(_) => self.missing_expr(),
|
||||
};
|
||||
let &FormatOptions {
|
||||
ref width,
|
||||
ref precision,
|
||||
alignment,
|
||||
fill,
|
||||
sign,
|
||||
alternate,
|
||||
zero_pad,
|
||||
debug_hex,
|
||||
} = &placeholder.format_options;
|
||||
let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));
|
||||
|
||||
let align = {
|
||||
let align = LangItem::FormatAlignment.ty_rel_path(
|
||||
self.db,
|
||||
self.krate,
|
||||
match alignment {
|
||||
Some(FormatAlignment::Left) => name![Left],
|
||||
Some(FormatAlignment::Right) => name![Right],
|
||||
Some(FormatAlignment::Center) => name![Center],
|
||||
None => name![Unknown],
|
||||
},
|
||||
);
|
||||
match align {
|
||||
Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
|
||||
None => self.missing_expr(),
|
||||
}
|
||||
};
|
||||
// This needs to match `Flag` in library/core/src/fmt/rt.rs.
|
||||
let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
|
||||
| ((sign == Some(FormatSign::Minus)) as u32) << 1
|
||||
| (alternate as u32) << 2
|
||||
| (zero_pad as u32) << 3
|
||||
| ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
|
||||
| ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
|
||||
let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
|
||||
flags as u128,
|
||||
Some(BuiltinUint::U32),
|
||||
)));
|
||||
let precision = self.make_count(&precision, argmap);
|
||||
let width = self.make_count(&width, argmap);
|
||||
|
||||
let format_placeholder_new = {
|
||||
let format_placeholder_new =
|
||||
LangItem::FormatPlaceholder.ty_rel_path(self.db, self.krate, name![new]);
|
||||
match format_placeholder_new {
|
||||
Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
|
||||
None => self.missing_expr(),
|
||||
}
|
||||
};
|
||||
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: format_placeholder_new,
|
||||
args: Box::new([position, fill, align, flags, precision, width]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate a hir expression for a format_args Count.
|
||||
///
|
||||
/// Generates:
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::rt::Count>::Is(…)
|
||||
/// ```
|
||||
///
|
||||
/// or
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::rt::Count>::Param(…)
|
||||
/// ```
|
||||
///
|
||||
/// or
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::rt::Count>::Implied
|
||||
/// ```
|
||||
fn make_count(
|
||||
&mut self,
|
||||
count: &Option<FormatCount>,
|
||||
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
||||
) -> ExprId {
|
||||
match count {
|
||||
Some(FormatCount::Literal(n)) => {
|
||||
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
|
||||
Some(count_is) => {
|
||||
let count_is = self.alloc_expr_desugared(Expr::Path(count_is));
|
||||
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
|
||||
*n as u128,
|
||||
Some(BuiltinUint::Usize),
|
||||
)));
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: count_is,
|
||||
args: Box::new([args]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
}
|
||||
None => self.missing_expr(),
|
||||
}
|
||||
}
|
||||
Some(FormatCount::Argument(arg)) => {
|
||||
if let Ok(arg_index) = arg.index {
|
||||
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
|
||||
|
||||
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) {
|
||||
Some(count_param) => {
|
||||
let count_param = self.alloc_expr_desugared(Expr::Path(count_param));
|
||||
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
|
||||
i as u128,
|
||||
Some(BuiltinUint::Usize),
|
||||
)));
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: count_param,
|
||||
args: Box::new([args]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
}
|
||||
None => self.missing_expr(),
|
||||
}
|
||||
} else {
|
||||
self.missing_expr()
|
||||
}
|
||||
}
|
||||
None => match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Implied]) {
|
||||
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
|
||||
None => self.missing_expr(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a hir expression representing an argument to a format_args invocation.
|
||||
///
|
||||
/// Generates:
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::Argument>::new_…(arg)
|
||||
/// ```
|
||||
fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
|
||||
use ArgumentType::*;
|
||||
use FormatTrait::*;
|
||||
match LangItem::FormatArgument.ty_rel_path(
|
||||
self.db,
|
||||
self.krate,
|
||||
match ty {
|
||||
Format(Display) => name![new_display],
|
||||
Format(Debug) => name![new_debug],
|
||||
Format(LowerExp) => name![new_lower_exp],
|
||||
Format(UpperExp) => name![new_upper_exp],
|
||||
Format(Octal) => name![new_octal],
|
||||
Format(Pointer) => name![new_pointer],
|
||||
Format(Binary) => name![new_binary],
|
||||
Format(LowerHex) => name![new_lower_hex],
|
||||
Format(UpperHex) => name![new_upper_hex],
|
||||
Usize => name![from_usize],
|
||||
},
|
||||
) {
|
||||
Some(new_fn) => {
|
||||
let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn));
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: new_fn,
|
||||
args: Box::new([arg]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
}
|
||||
None => self.missing_expr(),
|
||||
}
|
||||
}
|
||||
// endregion: format
|
||||
}
|
||||
|
||||
fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
|
||||
|
@ -1606,3 +2033,9 @@ fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
|
|||
(|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
|
||||
.map_or(false, |it| it.kind() == syntax::T![,])
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
enum ArgumentType {
|
||||
Format(FormatTrait),
|
||||
Usize,
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use hir_expand::db::ExpandDatabase;
|
||||
use itertools::Itertools;
|
||||
use syntax::ast::HasName;
|
||||
|
||||
use crate::{
|
||||
|
@ -51,8 +51,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
|
|||
}
|
||||
};
|
||||
|
||||
let mut p =
|
||||
Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
|
||||
let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
|
||||
if let DefWithBodyId::FunctionId(it) = owner {
|
||||
p.buf.push('(');
|
||||
body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| {
|
||||
|
@ -76,8 +75,7 @@ pub(super) fn print_expr_hir(
|
|||
_owner: DefWithBodyId,
|
||||
expr: ExprId,
|
||||
) -> String {
|
||||
let mut p =
|
||||
Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
|
||||
let mut p = Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false };
|
||||
p.print_expr(expr);
|
||||
p.buf
|
||||
}
|
||||
|
@ -98,7 +96,7 @@ macro_rules! wln {
|
|||
}
|
||||
|
||||
struct Printer<'a> {
|
||||
db: &'a dyn ExpandDatabase,
|
||||
db: &'a dyn DefDatabase,
|
||||
body: &'a Body,
|
||||
buf: String,
|
||||
indent_level: usize,
|
||||
|
@ -142,9 +140,14 @@ impl Printer<'_> {
|
|||
}
|
||||
|
||||
fn newline(&mut self) {
|
||||
match self.buf.chars().rev().find(|ch| *ch != ' ') {
|
||||
Some('\n') | None => {}
|
||||
_ => writeln!(self).unwrap(),
|
||||
match self.buf.chars().rev().find_position(|ch| *ch != ' ') {
|
||||
Some((_, '\n')) | None => {}
|
||||
Some((idx, _)) => {
|
||||
if idx != 0 {
|
||||
self.buf.drain(self.buf.len() - idx..);
|
||||
}
|
||||
writeln!(self).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,6 +157,19 @@ impl Printer<'_> {
|
|||
match expr {
|
||||
Expr::Missing => w!(self, "<EFBFBD>"),
|
||||
Expr::Underscore => w!(self, "_"),
|
||||
Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
|
||||
Expr::OffsetOf(offset_of) => {
|
||||
w!(self, "builtin#offset_of(");
|
||||
self.print_type_ref(&offset_of.container);
|
||||
w!(
|
||||
self,
|
||||
", {})",
|
||||
offset_of
|
||||
.fields
|
||||
.iter()
|
||||
.format_with(".", |field, f| f(&field.display(self.db.upcast())))
|
||||
);
|
||||
}
|
||||
Expr::Path(path) => self.print_path(path),
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
w!(self, "if ");
|
||||
|
@ -173,7 +189,7 @@ impl Printer<'_> {
|
|||
}
|
||||
Expr::Loop { body, label } => {
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}: ", self.body[*lbl].name.display(self.db));
|
||||
w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast()));
|
||||
}
|
||||
w!(self, "loop ");
|
||||
self.print_expr(*body);
|
||||
|
@ -193,7 +209,7 @@ impl Printer<'_> {
|
|||
}
|
||||
Expr::MethodCall { receiver, method_name, args, generic_args } => {
|
||||
self.print_expr(*receiver);
|
||||
w!(self, ".{}", method_name.display(self.db));
|
||||
w!(self, ".{}", method_name.display(self.db.upcast()));
|
||||
if let Some(args) = generic_args {
|
||||
w!(self, "::<");
|
||||
print_generic_args(self.db, args, self).unwrap();
|
||||
|
@ -231,13 +247,13 @@ impl Printer<'_> {
|
|||
Expr::Continue { label } => {
|
||||
w!(self, "continue");
|
||||
if let Some(lbl) = label {
|
||||
w!(self, " {}", self.body[*lbl].name.display(self.db));
|
||||
w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
|
||||
}
|
||||
}
|
||||
Expr::Break { expr, label } => {
|
||||
w!(self, "break");
|
||||
if let Some(lbl) = label {
|
||||
w!(self, " {}", self.body[*lbl].name.display(self.db));
|
||||
w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
|
||||
}
|
||||
if let Some(expr) = expr {
|
||||
self.whitespace();
|
||||
|
@ -276,7 +292,7 @@ impl Printer<'_> {
|
|||
w!(self, "{{");
|
||||
self.indented(|p| {
|
||||
for field in &**fields {
|
||||
w!(p, "{}: ", field.name.display(self.db));
|
||||
w!(p, "{}: ", field.name.display(self.db.upcast()));
|
||||
p.print_expr(field.expr);
|
||||
wln!(p, ",");
|
||||
}
|
||||
|
@ -293,7 +309,7 @@ impl Printer<'_> {
|
|||
}
|
||||
Expr::Field { expr, name } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, ".{}", name.display(self.db));
|
||||
w!(self, ".{}", name.display(self.db.upcast()));
|
||||
}
|
||||
Expr::Await { expr } => {
|
||||
self.print_expr(*expr);
|
||||
|
@ -431,7 +447,8 @@ impl Printer<'_> {
|
|||
}
|
||||
Expr::Literal(lit) => self.print_literal(lit),
|
||||
Expr::Block { id: _, statements, tail, label } => {
|
||||
let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
|
||||
let label =
|
||||
label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db.upcast())));
|
||||
self.print_block(label.as_deref(), statements, tail);
|
||||
}
|
||||
Expr::Unsafe { id: _, statements, tail } => {
|
||||
|
@ -507,7 +524,7 @@ impl Printer<'_> {
|
|||
w!(self, " {{");
|
||||
self.indented(|p| {
|
||||
for arg in args.iter() {
|
||||
w!(p, "{}: ", arg.name.display(self.db));
|
||||
w!(p, "{}: ", arg.name.display(self.db.upcast()));
|
||||
p.print_pat(arg.pat);
|
||||
wln!(p, ",");
|
||||
}
|
||||
|
@ -666,6 +683,6 @@ impl Printer<'_> {
|
|||
BindingAnnotation::Ref => "ref ",
|
||||
BindingAnnotation::RefMut => "ref mut ",
|
||||
};
|
||||
w!(self, "{}{}", mode, name.display(self.db));
|
||||
w!(self, "{}{}", mode, name.display(self.db.upcast()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
mod block;
|
||||
|
||||
use base_db::{fixture::WithFixture, SourceDatabase};
|
||||
use expect_test::Expect;
|
||||
use expect_test::{expect, Expect};
|
||||
|
||||
use crate::{test_db::TestDB, ModuleDefId};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn lower(ra_fixture: &str) -> Arc<Body> {
|
||||
fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
|
@ -21,8 +21,10 @@ fn lower(ra_fixture: &str) -> Arc<Body> {
|
|||
}
|
||||
}
|
||||
}
|
||||
let fn_def = fn_def.unwrap().into();
|
||||
|
||||
db.body(fn_def.unwrap().into())
|
||||
let body = db.body(fn_def);
|
||||
(db, body, fn_def)
|
||||
}
|
||||
|
||||
fn def_map_at(ra_fixture: &str) -> String {
|
||||
|
@ -138,3 +140,84 @@ mod m {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_builtin_format_args() {
|
||||
// Regression test for a path resolution bug introduced with inner item handling.
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: fmt
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
expect![[r#"
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"\"hello ", " ", " friends, we ", " ", "", "\"",
|
||||
],
|
||||
&[
|
||||
builtin#lang(Argument::new_display)(
|
||||
&count,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
&"fancy",
|
||||
), builtin#lang(Argument::new_debug)(
|
||||
&are,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
&"!",
|
||||
),
|
||||
],
|
||||
&[
|
||||
builtin#lang(Placeholder::new)(
|
||||
0usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
8u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Is)(
|
||||
2usize,
|
||||
),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
1usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
2usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
1usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
3usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
},
|
||||
);
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def))
|
||||
}
|
||||
|
|
|
@ -447,6 +447,7 @@ impl VariantData {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: Linear lookup
|
||||
pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
|
||||
self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
|
||||
}
|
||||
|
|
|
@ -37,6 +37,20 @@ pub fn find_path_prefixed(
|
|||
find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum Stability {
|
||||
Unstable,
|
||||
Stable,
|
||||
}
|
||||
use Stability::*;
|
||||
|
||||
fn zip_stability(a: Stability, b: Stability) -> Stability {
|
||||
match (a, b) {
|
||||
(Stable, Stable) => Stable,
|
||||
_ => Unstable,
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_PATH_LEN: usize = 15;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -95,7 +109,8 @@ fn find_path_inner(
|
|||
MAX_PATH_LEN,
|
||||
prefixed,
|
||||
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
|
||||
);
|
||||
)
|
||||
.map(|(item, _)| item);
|
||||
}
|
||||
|
||||
// - if the item is already in scope, return the name under which it is
|
||||
|
@ -143,6 +158,7 @@ fn find_path_inner(
|
|||
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
|
||||
scope_name,
|
||||
)
|
||||
.map(|(item, _)| item)
|
||||
}
|
||||
|
||||
fn find_path_for_module(
|
||||
|
@ -155,7 +171,7 @@ fn find_path_for_module(
|
|||
max_len: usize,
|
||||
prefixed: Option<PrefixKind>,
|
||||
prefer_no_std: bool,
|
||||
) -> Option<ModPath> {
|
||||
) -> Option<(ModPath, Stability)> {
|
||||
if max_len == 0 {
|
||||
return None;
|
||||
}
|
||||
|
@ -165,19 +181,19 @@ fn find_path_for_module(
|
|||
let scope_name = find_in_scope(db, def_map, from, ItemInNs::Types(module_id.into()));
|
||||
if prefixed.is_none() {
|
||||
if let Some(scope_name) = scope_name {
|
||||
return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name)));
|
||||
return Some((ModPath::from_segments(PathKind::Plain, Some(scope_name)), Stable));
|
||||
}
|
||||
}
|
||||
|
||||
// - if the item is the crate root, return `crate`
|
||||
if module_id == crate_root {
|
||||
return Some(ModPath::from_segments(PathKind::Crate, None));
|
||||
return Some((ModPath::from_segments(PathKind::Crate, None), Stable));
|
||||
}
|
||||
|
||||
// - if relative paths are fine, check if we are searching for a parent
|
||||
if prefixed.filter(PrefixKind::is_absolute).is_none() {
|
||||
if let modpath @ Some(_) = find_self_super(def_map, module_id, from) {
|
||||
return modpath;
|
||||
return modpath.zip(Some(Stable));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,14 +217,14 @@ fn find_path_for_module(
|
|||
} else {
|
||||
PathKind::Plain
|
||||
};
|
||||
return Some(ModPath::from_segments(kind, Some(name)));
|
||||
return Some((ModPath::from_segments(kind, Some(name)), Stable));
|
||||
}
|
||||
}
|
||||
|
||||
if let value @ Some(_) =
|
||||
find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from)
|
||||
{
|
||||
return value;
|
||||
return value.zip(Some(Stable));
|
||||
}
|
||||
calculate_best_path(
|
||||
db,
|
||||
|
@ -301,11 +317,19 @@ fn calculate_best_path(
|
|||
mut prefixed: Option<PrefixKind>,
|
||||
prefer_no_std: bool,
|
||||
scope_name: Option<Name>,
|
||||
) -> Option<ModPath> {
|
||||
) -> Option<(ModPath, Stability)> {
|
||||
if max_len <= 1 {
|
||||
return None;
|
||||
}
|
||||
let mut best_path = None;
|
||||
let update_best_path =
|
||||
|best_path: &mut Option<_>, new_path: (ModPath, Stability)| match best_path {
|
||||
Some((old_path, old_stability)) => {
|
||||
*old_path = new_path.0;
|
||||
*old_stability = zip_stability(*old_stability, new_path.1);
|
||||
}
|
||||
None => *best_path = Some(new_path),
|
||||
};
|
||||
// Recursive case:
|
||||
// - otherwise, look for modules containing (reexporting) it and import it from one of those
|
||||
if item.krate(db) == Some(from.krate) {
|
||||
|
@ -328,14 +352,14 @@ fn calculate_best_path(
|
|||
prefixed,
|
||||
prefer_no_std,
|
||||
) {
|
||||
path.push_segment(name);
|
||||
path.0.push_segment(name);
|
||||
|
||||
let new_path = match best_path {
|
||||
let new_path = match best_path.take() {
|
||||
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
|
||||
None => path,
|
||||
};
|
||||
best_path_len = new_path.len();
|
||||
best_path = Some(new_path);
|
||||
best_path_len = new_path.0.len();
|
||||
update_best_path(&mut best_path, new_path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -354,7 +378,7 @@ fn calculate_best_path(
|
|||
|
||||
// Determine best path for containing module and append last segment from `info`.
|
||||
// FIXME: we should guide this to look up the path locally, or from the same crate again?
|
||||
let mut path = find_path_for_module(
|
||||
let (mut path, path_stability) = find_path_for_module(
|
||||
db,
|
||||
def_map,
|
||||
visited_modules,
|
||||
|
@ -367,16 +391,19 @@ fn calculate_best_path(
|
|||
)?;
|
||||
cov_mark::hit!(partially_imported);
|
||||
path.push_segment(info.name.clone());
|
||||
Some(path)
|
||||
Some((
|
||||
path,
|
||||
zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }),
|
||||
))
|
||||
})
|
||||
});
|
||||
|
||||
for path in extern_paths {
|
||||
let new_path = match best_path {
|
||||
let new_path = match best_path.take() {
|
||||
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
|
||||
None => path,
|
||||
};
|
||||
best_path = Some(new_path);
|
||||
update_best_path(&mut best_path, new_path);
|
||||
}
|
||||
}
|
||||
if let Some(module) = item.module(db) {
|
||||
|
@ -387,15 +414,24 @@ fn calculate_best_path(
|
|||
}
|
||||
match prefixed.map(PrefixKind::prefix) {
|
||||
Some(prefix) => best_path.or_else(|| {
|
||||
scope_name.map(|scope_name| ModPath::from_segments(prefix, Some(scope_name)))
|
||||
scope_name.map(|scope_name| (ModPath::from_segments(prefix, Some(scope_name)), Stable))
|
||||
}),
|
||||
None => best_path,
|
||||
}
|
||||
}
|
||||
|
||||
fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
|
||||
fn select_best_path(
|
||||
old_path: (ModPath, Stability),
|
||||
new_path: (ModPath, Stability),
|
||||
prefer_no_std: bool,
|
||||
) -> (ModPath, Stability) {
|
||||
match (old_path.1, new_path.1) {
|
||||
(Stable, Unstable) => return old_path,
|
||||
(Unstable, Stable) => return new_path,
|
||||
_ => {}
|
||||
}
|
||||
const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc];
|
||||
match (old_path.segments().first(), new_path.segments().first()) {
|
||||
match (old_path.0.segments().first(), new_path.0.segments().first()) {
|
||||
(Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => {
|
||||
let rank = match prefer_no_std {
|
||||
false => |name: &Name| match name {
|
||||
|
@ -416,7 +452,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
|
|||
match nrank.cmp(&orank) {
|
||||
Ordering::Less => old_path,
|
||||
Ordering::Equal => {
|
||||
if new_path.len() < old_path.len() {
|
||||
if new_path.0.len() < old_path.0.len() {
|
||||
new_path
|
||||
} else {
|
||||
old_path
|
||||
|
@ -426,7 +462,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
if new_path.len() < old_path.len() {
|
||||
if new_path.0.len() < old_path.0.len() {
|
||||
new_path
|
||||
} else {
|
||||
old_path
|
||||
|
@ -1360,4 +1396,29 @@ pub mod ops {
|
|||
"std::ops::Deref",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn respect_unstable_modules() {
|
||||
check_found_path(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std,core
|
||||
#![no_std]
|
||||
extern crate std;
|
||||
$0
|
||||
//- /longer.rs crate:std deps:core
|
||||
pub mod error {
|
||||
pub use core::error::Error;
|
||||
}
|
||||
//- /core.rs crate:core
|
||||
pub mod error {
|
||||
#![unstable(feature = "error_in_core", issue = "103765")]
|
||||
pub trait Error {}
|
||||
}
|
||||
"#,
|
||||
"std::error::Error",
|
||||
"std::error::Error",
|
||||
"std::error::Error",
|
||||
"std::error::Error",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
//! See also a neighboring `body` module.
|
||||
|
||||
pub mod type_ref;
|
||||
pub mod format_args;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
|
@ -117,7 +118,6 @@ impl From<ast::LiteralKind> for Literal {
|
|||
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
|
||||
use ast::LiteralKind;
|
||||
match ast_lit_kind {
|
||||
// FIXME: these should have actual values filled in, but unsure on perf impact
|
||||
LiteralKind::IntNumber(lit) => {
|
||||
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
|
||||
Literal::Float(
|
||||
|
@ -281,6 +281,19 @@ pub enum Expr {
|
|||
Array(Array),
|
||||
Literal(Literal),
|
||||
Underscore,
|
||||
OffsetOf(OffsetOf),
|
||||
InlineAsm(InlineAsm),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OffsetOf {
|
||||
pub container: Interned<TypeRef>,
|
||||
pub fields: Box<[Name]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InlineAsm {
|
||||
pub e: ExprId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -341,7 +354,8 @@ impl Expr {
|
|||
pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
|
||||
match self {
|
||||
Expr::Missing => {}
|
||||
Expr::Path(_) => {}
|
||||
Expr::Path(_) | Expr::OffsetOf(_) => {}
|
||||
Expr::InlineAsm(it) => f(it.e),
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
f(*condition);
|
||||
f(*then_branch);
|
||||
|
|
502
crates/hir-def/src/hir/format_args.rs
Normal file
502
crates/hir-def/src/hir/format_args.rs
Normal file
|
@ -0,0 +1,502 @@
|
|||
//! Parses `format_args` input.
|
||||
use std::mem;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use rustc_parse_format as parse;
|
||||
use syntax::{
|
||||
ast::{self, IsString},
|
||||
AstToken, SmolStr, TextRange,
|
||||
};
|
||||
|
||||
use crate::hir::ExprId;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FormatArgs {
|
||||
pub template: Box<[FormatArgsPiece]>,
|
||||
pub arguments: FormatArguments,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FormatArguments {
|
||||
pub arguments: Box<[FormatArgument]>,
|
||||
pub num_unnamed_args: usize,
|
||||
pub num_explicit_args: usize,
|
||||
pub names: Box<[(Name, usize)]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum FormatArgsPiece {
|
||||
Literal(Box<str>),
|
||||
Placeholder(FormatPlaceholder),
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FormatPlaceholder {
|
||||
/// Index into [`FormatArgs::arguments`].
|
||||
pub argument: FormatArgPosition,
|
||||
/// The span inside the format string for the full `{…}` placeholder.
|
||||
pub span: Option<TextRange>,
|
||||
/// `{}`, `{:?}`, or `{:x}`, etc.
|
||||
pub format_trait: FormatTrait,
|
||||
/// `{}` or `{:.5}` or `{:-^20}`, etc.
|
||||
pub format_options: FormatOptions,
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FormatArgPosition {
|
||||
/// Which argument this position refers to (Ok),
|
||||
/// or would've referred to if it existed (Err).
|
||||
pub index: Result<usize, usize>,
|
||||
/// What kind of position this is. See [`FormatArgPositionKind`].
|
||||
pub kind: FormatArgPositionKind,
|
||||
/// The span of the name or number.
|
||||
pub span: Option<TextRange>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum FormatArgPositionKind {
|
||||
/// `{}` or `{:.*}`
|
||||
Implicit,
|
||||
/// `{1}` or `{:1$}` or `{:.1$}`
|
||||
Number,
|
||||
/// `{a}` or `{:a$}` or `{:.a$}`
|
||||
Named,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum FormatTrait {
|
||||
/// `{}`
|
||||
Display,
|
||||
/// `{:?}`
|
||||
Debug,
|
||||
/// `{:e}`
|
||||
LowerExp,
|
||||
/// `{:E}`
|
||||
UpperExp,
|
||||
/// `{:o}`
|
||||
Octal,
|
||||
/// `{:p}`
|
||||
Pointer,
|
||||
/// `{:b}`
|
||||
Binary,
|
||||
/// `{:x}`
|
||||
LowerHex,
|
||||
/// `{:X}`
|
||||
UpperHex,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
|
||||
pub struct FormatOptions {
|
||||
/// The width. E.g. `{:5}` or `{:width$}`.
|
||||
pub width: Option<FormatCount>,
|
||||
/// The precision. E.g. `{:.5}` or `{:.precision$}`.
|
||||
pub precision: Option<FormatCount>,
|
||||
/// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`.
|
||||
pub alignment: Option<FormatAlignment>,
|
||||
/// The fill character. E.g. the `.` in `{:.>10}`.
|
||||
pub fill: Option<char>,
|
||||
/// The `+` or `-` flag.
|
||||
pub sign: Option<FormatSign>,
|
||||
/// The `#` flag.
|
||||
pub alternate: bool,
|
||||
/// The `0` flag. E.g. the `0` in `{:02x}`.
|
||||
pub zero_pad: bool,
|
||||
/// The `x` or `X` flag (for `Debug` only). E.g. the `x` in `{:x?}`.
|
||||
pub debug_hex: Option<FormatDebugHex>,
|
||||
}
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FormatSign {
|
||||
/// The `+` flag.
|
||||
Plus,
|
||||
/// The `-` flag.
|
||||
Minus,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FormatDebugHex {
|
||||
/// The `x` flag in `{:x?}`.
|
||||
Lower,
|
||||
/// The `X` flag in `{:X?}`.
|
||||
Upper,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FormatAlignment {
|
||||
/// `{:<}`
|
||||
Left,
|
||||
/// `{:>}`
|
||||
Right,
|
||||
/// `{:^}`
|
||||
Center,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FormatCount {
|
||||
/// `{:5}` or `{:.5}`
|
||||
Literal(usize),
|
||||
/// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
|
||||
Argument(FormatArgPosition),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FormatArgument {
|
||||
pub kind: FormatArgumentKind,
|
||||
pub expr: ExprId,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum FormatArgumentKind {
|
||||
/// `format_args(…, arg)`
|
||||
Normal,
|
||||
/// `format_args(…, arg = 1)`
|
||||
Named(Name),
|
||||
/// `format_args("… {arg} …")`
|
||||
Captured(Name),
|
||||
}
|
||||
|
||||
// Only used in parse_args and report_invalid_references,
|
||||
// to indicate how a referred argument was used.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum PositionUsedAs {
|
||||
Placeholder(Option<TextRange>),
|
||||
Precision,
|
||||
Width,
|
||||
}
|
||||
use PositionUsedAs::*;
|
||||
|
||||
pub(crate) fn parse(
|
||||
s: &ast::String,
|
||||
fmt_snippet: Option<String>,
|
||||
mut args: FormatArgumentsCollector,
|
||||
is_direct_literal: bool,
|
||||
mut synth: impl FnMut(Name) -> ExprId,
|
||||
) -> FormatArgs {
|
||||
let text = s.text();
|
||||
let str_style = match s.quote_offsets() {
|
||||
Some(offsets) => {
|
||||
let raw = u32::from(offsets.quotes.0.len()) - 1;
|
||||
(raw != 0).then_some(raw as usize)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let mut parser =
|
||||
parse::Parser::new(text, str_style, fmt_snippet, false, parse::ParseMode::Format);
|
||||
|
||||
let mut pieces = Vec::new();
|
||||
while let Some(piece) = parser.next() {
|
||||
if !parser.errors.is_empty() {
|
||||
break;
|
||||
} else {
|
||||
pieces.push(piece);
|
||||
}
|
||||
}
|
||||
let is_source_literal = parser.is_source_literal;
|
||||
if !parser.errors.is_empty() {
|
||||
// FIXME: Diagnose
|
||||
return FormatArgs { template: Default::default(), arguments: args.finish() };
|
||||
}
|
||||
|
||||
let to_span = |inner_span: parse::InnerSpan| {
|
||||
is_source_literal.then(|| {
|
||||
TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
|
||||
})
|
||||
};
|
||||
|
||||
let mut used = vec![false; args.explicit_args().len()];
|
||||
let mut invalid_refs = Vec::new();
|
||||
let mut numeric_refences_to_named_arg = Vec::new();
|
||||
|
||||
enum ArgRef<'a> {
|
||||
Index(usize),
|
||||
Name(&'a str, Option<TextRange>),
|
||||
}
|
||||
let mut lookup_arg = |arg: ArgRef<'_>,
|
||||
span: Option<TextRange>,
|
||||
used_as: PositionUsedAs,
|
||||
kind: FormatArgPositionKind|
|
||||
-> FormatArgPosition {
|
||||
let index = match arg {
|
||||
ArgRef::Index(index) => {
|
||||
if let Some(arg) = args.by_index(index) {
|
||||
used[index] = true;
|
||||
if arg.kind.ident().is_some() {
|
||||
// This was a named argument, but it was used as a positional argument.
|
||||
numeric_refences_to_named_arg.push((index, span, used_as));
|
||||
}
|
||||
Ok(index)
|
||||
} else {
|
||||
// Doesn't exist as an explicit argument.
|
||||
invalid_refs.push((index, span, used_as, kind));
|
||||
Err(index)
|
||||
}
|
||||
}
|
||||
ArgRef::Name(name, _span) => {
|
||||
let name = Name::new_text_dont_use(SmolStr::new(name));
|
||||
if let Some((index, _)) = args.by_name(&name) {
|
||||
// Name found in `args`, so we resolve it to its index.
|
||||
if index < args.explicit_args().len() {
|
||||
// Mark it as used, if it was an explicit argument.
|
||||
used[index] = true;
|
||||
}
|
||||
Ok(index)
|
||||
} else {
|
||||
// Name not found in `args`, so we add it as an implicitly captured argument.
|
||||
if !is_direct_literal {
|
||||
// For the moment capturing variables from format strings expanded from macros is
|
||||
// disabled (see RFC #2795)
|
||||
// FIXME: Diagnose
|
||||
}
|
||||
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),
|
||||
}))
|
||||
}
|
||||
}
|
||||
};
|
||||
FormatArgPosition { index, kind, span }
|
||||
};
|
||||
|
||||
let mut template = Vec::new();
|
||||
let mut unfinished_literal = String::new();
|
||||
let mut placeholder_index = 0;
|
||||
|
||||
for piece in pieces {
|
||||
match piece {
|
||||
parse::Piece::String(s) => {
|
||||
unfinished_literal.push_str(s);
|
||||
}
|
||||
parse::Piece::NextArgument(arg) => {
|
||||
let parse::Argument { position, position_span, format } = *arg;
|
||||
if !unfinished_literal.is_empty() {
|
||||
template.push(FormatArgsPiece::Literal(
|
||||
mem::take(&mut unfinished_literal).into_boxed_str(),
|
||||
));
|
||||
}
|
||||
|
||||
let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s));
|
||||
placeholder_index += 1;
|
||||
|
||||
let position_span = to_span(position_span);
|
||||
let argument = match position {
|
||||
parse::ArgumentImplicitlyIs(i) => lookup_arg(
|
||||
ArgRef::Index(i),
|
||||
position_span,
|
||||
Placeholder(span),
|
||||
FormatArgPositionKind::Implicit,
|
||||
),
|
||||
parse::ArgumentIs(i) => lookup_arg(
|
||||
ArgRef::Index(i),
|
||||
position_span,
|
||||
Placeholder(span),
|
||||
FormatArgPositionKind::Number,
|
||||
),
|
||||
parse::ArgumentNamed(name) => lookup_arg(
|
||||
ArgRef::Name(name, position_span),
|
||||
position_span,
|
||||
Placeholder(span),
|
||||
FormatArgPositionKind::Named,
|
||||
),
|
||||
};
|
||||
|
||||
let alignment = match format.align {
|
||||
parse::AlignUnknown => None,
|
||||
parse::AlignLeft => Some(FormatAlignment::Left),
|
||||
parse::AlignRight => Some(FormatAlignment::Right),
|
||||
parse::AlignCenter => Some(FormatAlignment::Center),
|
||||
};
|
||||
|
||||
let format_trait = match format.ty {
|
||||
"" => FormatTrait::Display,
|
||||
"?" => FormatTrait::Debug,
|
||||
"e" => FormatTrait::LowerExp,
|
||||
"E" => FormatTrait::UpperExp,
|
||||
"o" => FormatTrait::Octal,
|
||||
"p" => FormatTrait::Pointer,
|
||||
"b" => FormatTrait::Binary,
|
||||
"x" => FormatTrait::LowerHex,
|
||||
"X" => FormatTrait::UpperHex,
|
||||
_ => {
|
||||
// FIXME: Diagnose
|
||||
FormatTrait::Display
|
||||
}
|
||||
};
|
||||
|
||||
let precision_span = format.precision_span.and_then(to_span);
|
||||
let precision = match format.precision {
|
||||
parse::CountIs(n) => Some(FormatCount::Literal(n)),
|
||||
parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
|
||||
ArgRef::Name(name, to_span(name_span)),
|
||||
precision_span,
|
||||
Precision,
|
||||
FormatArgPositionKind::Named,
|
||||
))),
|
||||
parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
|
||||
ArgRef::Index(i),
|
||||
precision_span,
|
||||
Precision,
|
||||
FormatArgPositionKind::Number,
|
||||
))),
|
||||
parse::CountIsStar(i) => Some(FormatCount::Argument(lookup_arg(
|
||||
ArgRef::Index(i),
|
||||
precision_span,
|
||||
Precision,
|
||||
FormatArgPositionKind::Implicit,
|
||||
))),
|
||||
parse::CountImplied => None,
|
||||
};
|
||||
|
||||
let width_span = format.width_span.and_then(to_span);
|
||||
let width = match format.width {
|
||||
parse::CountIs(n) => Some(FormatCount::Literal(n)),
|
||||
parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
|
||||
ArgRef::Name(name, to_span(name_span)),
|
||||
width_span,
|
||||
Width,
|
||||
FormatArgPositionKind::Named,
|
||||
))),
|
||||
parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
|
||||
ArgRef::Index(i),
|
||||
width_span,
|
||||
Width,
|
||||
FormatArgPositionKind::Number,
|
||||
))),
|
||||
parse::CountIsStar(_) => unreachable!(),
|
||||
parse::CountImplied => None,
|
||||
};
|
||||
|
||||
template.push(FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
argument,
|
||||
span,
|
||||
format_trait,
|
||||
format_options: FormatOptions {
|
||||
fill: format.fill,
|
||||
alignment,
|
||||
sign: format.sign.map(|s| match s {
|
||||
parse::Sign::Plus => FormatSign::Plus,
|
||||
parse::Sign::Minus => FormatSign::Minus,
|
||||
}),
|
||||
alternate: format.alternate,
|
||||
zero_pad: format.zero_pad,
|
||||
debug_hex: format.debug_hex.map(|s| match s {
|
||||
parse::DebugHex::Lower => FormatDebugHex::Lower,
|
||||
parse::DebugHex::Upper => FormatDebugHex::Upper,
|
||||
}),
|
||||
precision,
|
||||
width,
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !unfinished_literal.is_empty() {
|
||||
template.push(FormatArgsPiece::Literal(unfinished_literal.into_boxed_str()));
|
||||
}
|
||||
|
||||
if !invalid_refs.is_empty() {
|
||||
// FIXME: Diagnose
|
||||
}
|
||||
|
||||
let unused = used
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, used)| !used)
|
||||
.map(|(i, _)| {
|
||||
let named = matches!(args.explicit_args()[i].kind, FormatArgumentKind::Named(_));
|
||||
(args.explicit_args()[i].expr, named)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !unused.is_empty() {
|
||||
// FIXME: Diagnose
|
||||
}
|
||||
|
||||
FormatArgs { template: template.into_boxed_slice(), arguments: args.finish() }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FormatArgumentsCollector {
|
||||
arguments: Vec<FormatArgument>,
|
||||
num_unnamed_args: usize,
|
||||
num_explicit_args: usize,
|
||||
names: Vec<(Name, usize)>,
|
||||
}
|
||||
|
||||
impl FormatArgumentsCollector {
|
||||
pub(crate) fn finish(self) -> FormatArguments {
|
||||
FormatArguments {
|
||||
arguments: self.arguments.into_boxed_slice(),
|
||||
num_unnamed_args: self.num_unnamed_args,
|
||||
num_explicit_args: self.num_explicit_args,
|
||||
names: self.names.into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self { arguments: vec![], names: vec![], num_unnamed_args: 0, num_explicit_args: 0 }
|
||||
}
|
||||
|
||||
pub fn add(&mut self, arg: FormatArgument) -> usize {
|
||||
let index = self.arguments.len();
|
||||
if let Some(name) = arg.kind.ident() {
|
||||
self.names.push((name.clone(), index));
|
||||
} else if self.names.is_empty() {
|
||||
// Only count the unnamed args before the first named arg.
|
||||
// (Any later ones are errors.)
|
||||
self.num_unnamed_args += 1;
|
||||
}
|
||||
if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
|
||||
// This is an explicit argument.
|
||||
// Make sure that all arguments so far are explicit.
|
||||
assert_eq!(
|
||||
self.num_explicit_args,
|
||||
self.arguments.len(),
|
||||
"captured arguments must be added last"
|
||||
);
|
||||
self.num_explicit_args += 1;
|
||||
}
|
||||
self.arguments.push(arg);
|
||||
index
|
||||
}
|
||||
|
||||
pub fn by_name(&self, name: &Name) -> Option<(usize, &FormatArgument)> {
|
||||
let &(_, i) = self.names.iter().find(|(n, _)| n == name)?;
|
||||
Some((i, &self.arguments[i]))
|
||||
}
|
||||
|
||||
pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {
|
||||
(i < self.num_explicit_args).then(|| &self.arguments[i])
|
||||
}
|
||||
|
||||
pub fn unnamed_args(&self) -> &[FormatArgument] {
|
||||
&self.arguments[..self.num_unnamed_args]
|
||||
}
|
||||
|
||||
pub fn named_args(&self) -> &[FormatArgument] {
|
||||
&self.arguments[self.num_unnamed_args..self.num_explicit_args]
|
||||
}
|
||||
|
||||
pub fn explicit_args(&self) -> &[FormatArgument] {
|
||||
&self.arguments[..self.num_explicit_args]
|
||||
}
|
||||
|
||||
pub fn all_args(&self) -> &[FormatArgument] {
|
||||
&self.arguments[..]
|
||||
}
|
||||
|
||||
pub fn all_args_mut(&mut self) -> &mut Vec<FormatArgument> {
|
||||
&mut self.arguments
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatArgumentKind {
|
||||
pub fn ident(&self) -> Option<&Name> {
|
||||
match self {
|
||||
Self::Normal => None,
|
||||
Self::Named(id) => Some(id),
|
||||
Self::Captured(id) => Some(id),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,8 @@ pub struct ImportInfo {
|
|||
pub is_trait_assoc_item: bool,
|
||||
/// Whether this item is annotated with `#[doc(hidden)]`.
|
||||
pub is_doc_hidden: bool,
|
||||
/// Whether this item is annotated with `#[unstable(..)]`.
|
||||
pub is_unstable: bool,
|
||||
}
|
||||
|
||||
/// A map from publicly exported items to its name.
|
||||
|
@ -108,12 +110,15 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
|
|||
|
||||
let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
|
||||
let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
|
||||
if per_ns.is_none() { None } else { Some((name, per_ns)) }
|
||||
if per_ns.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some((name, per_ns))
|
||||
}
|
||||
});
|
||||
|
||||
for (name, per_ns) in visible_items {
|
||||
for (item, import) in per_ns.iter_items() {
|
||||
// FIXME: Not yet used, but will be once we handle doc(hidden) import sources
|
||||
let attr_id = if let Some(import) = import {
|
||||
match import {
|
||||
ImportOrExternCrate::ExternCrate(id) => Some(id.into()),
|
||||
|
@ -125,28 +130,59 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
|
|||
ItemInNs::Macros(id) => Some(id.into()),
|
||||
}
|
||||
};
|
||||
let is_doc_hidden =
|
||||
attr_id.map_or(false, |attr_id| db.attrs(attr_id).has_doc_hidden());
|
||||
let status @ (is_doc_hidden, is_unstable) =
|
||||
attr_id.map_or((false, false), |attr_id| {
|
||||
let attrs = db.attrs(attr_id);
|
||||
(attrs.has_doc_hidden(), attrs.is_unstable())
|
||||
});
|
||||
|
||||
let import_info = ImportInfo {
|
||||
name: name.clone(),
|
||||
container: module,
|
||||
is_trait_assoc_item: false,
|
||||
is_doc_hidden,
|
||||
is_unstable,
|
||||
};
|
||||
|
||||
match depth_map.entry(item) {
|
||||
Entry::Vacant(entry) => _ = entry.insert((depth, is_doc_hidden)),
|
||||
Entry::Vacant(entry) => _ = entry.insert((depth, status)),
|
||||
Entry::Occupied(mut entry) => {
|
||||
let &(occ_depth, occ_is_doc_hidden) = entry.get();
|
||||
// Prefer the one that is not doc(hidden),
|
||||
// Otherwise, if both have the same doc(hidden)-ness and the new path is shorter, prefer that one.
|
||||
let overwrite_entry = occ_is_doc_hidden && !is_doc_hidden
|
||||
|| occ_is_doc_hidden == is_doc_hidden && depth < occ_depth;
|
||||
if !overwrite_entry {
|
||||
let &(occ_depth, (occ_is_doc_hidden, occ_is_unstable)) = entry.get();
|
||||
(depth, occ_depth);
|
||||
let overwrite = match (
|
||||
is_doc_hidden,
|
||||
occ_is_doc_hidden,
|
||||
is_unstable,
|
||||
occ_is_unstable,
|
||||
) {
|
||||
// no change of hiddeness or unstableness
|
||||
(true, true, true, true)
|
||||
| (true, true, false, false)
|
||||
| (false, false, true, true)
|
||||
| (false, false, false, false) => depth < occ_depth,
|
||||
|
||||
// either less hidden or less unstable, accept
|
||||
(true, true, false, true)
|
||||
| (false, true, true, true)
|
||||
| (false, true, false, true)
|
||||
| (false, true, false, false)
|
||||
| (false, false, false, true) => true,
|
||||
// more hidden or unstable, discard
|
||||
(true, true, true, false)
|
||||
| (true, false, true, true)
|
||||
| (true, false, true, false)
|
||||
| (true, false, false, false)
|
||||
| (false, false, true, false) => false,
|
||||
|
||||
// exchanges doc(hidden) for unstable (and vice-versa),
|
||||
(true, false, false, true) | (false, true, true, false) => {
|
||||
depth < occ_depth
|
||||
}
|
||||
};
|
||||
if !overwrite {
|
||||
continue;
|
||||
}
|
||||
entry.insert((depth, is_doc_hidden));
|
||||
entry.insert((depth, status));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +207,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
map.shrink_to_fit();
|
||||
map
|
||||
}
|
||||
|
||||
|
@ -200,11 +236,13 @@ fn collect_trait_assoc_items(
|
|||
ItemInNs::Values(module_def_id)
|
||||
};
|
||||
|
||||
let attrs = &db.attrs(item.into());
|
||||
let assoc_item_info = ImportInfo {
|
||||
container: trait_import_info.container,
|
||||
name: assoc_item_name.clone(),
|
||||
is_trait_assoc_item: true,
|
||||
is_doc_hidden: db.attrs(item.into()).has_doc_hidden(),
|
||||
is_doc_hidden: attrs.has_doc_hidden(),
|
||||
is_unstable: attrs.is_unstable(),
|
||||
};
|
||||
map.insert(assoc_item, assoc_item_info);
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ impl ItemTree {
|
|||
}
|
||||
|
||||
pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
|
||||
pretty::print_item_tree(db.upcast(), self)
|
||||
pretty::print_item_tree(db, self)
|
||||
}
|
||||
|
||||
fn data(&self) -> &ItemTreeData {
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use hir_expand::db::ExpandDatabase;
|
||||
|
||||
use crate::{
|
||||
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
|
||||
pretty::{print_path, print_type_bounds, print_type_ref},
|
||||
|
@ -12,7 +10,7 @@ use crate::{
|
|||
|
||||
use super::*;
|
||||
|
||||
pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
|
||||
pub(super) fn print_item_tree(db: &dyn DefDatabase, tree: &ItemTree) -> String {
|
||||
let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
|
||||
|
||||
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
|
||||
|
@ -45,7 +43,7 @@ macro_rules! wln {
|
|||
}
|
||||
|
||||
struct Printer<'a> {
|
||||
db: &'a dyn ExpandDatabase,
|
||||
db: &'a dyn DefDatabase,
|
||||
tree: &'a ItemTree,
|
||||
buf: String,
|
||||
indent_level: usize,
|
||||
|
@ -91,7 +89,7 @@ impl Printer<'_> {
|
|||
self,
|
||||
"#{}[{}{}]{}",
|
||||
inner,
|
||||
attr.path.display(self.db),
|
||||
attr.path.display(self.db.upcast()),
|
||||
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
|
||||
separated_by,
|
||||
);
|
||||
|
@ -106,7 +104,7 @@ impl Printer<'_> {
|
|||
|
||||
fn print_visibility(&mut self, vis: RawVisibilityId) {
|
||||
match &self.tree[vis] {
|
||||
RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)),
|
||||
RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db.upcast())),
|
||||
RawVisibility::Public => w!(self, "pub "),
|
||||
};
|
||||
}
|
||||
|
@ -121,7 +119,7 @@ impl Printer<'_> {
|
|||
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
||||
this.print_attrs_of(field, "\n");
|
||||
this.print_visibility(*visibility);
|
||||
w!(this, "{}: ", name.display(self.db));
|
||||
w!(this, "{}: ", name.display(self.db.upcast()));
|
||||
this.print_type_ref(type_ref);
|
||||
wln!(this, ",");
|
||||
}
|
||||
|
@ -135,7 +133,7 @@ impl Printer<'_> {
|
|||
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
||||
this.print_attrs_of(field, "\n");
|
||||
this.print_visibility(*visibility);
|
||||
w!(this, "{}: ", name.display(self.db));
|
||||
w!(this, "{}: ", name.display(self.db.upcast()));
|
||||
this.print_type_ref(type_ref);
|
||||
wln!(this, ",");
|
||||
}
|
||||
|
@ -168,20 +166,20 @@ impl Printer<'_> {
|
|||
fn print_use_tree(&mut self, use_tree: &UseTree) {
|
||||
match &use_tree.kind {
|
||||
UseTreeKind::Single { path, alias } => {
|
||||
w!(self, "{}", path.display(self.db));
|
||||
w!(self, "{}", path.display(self.db.upcast()));
|
||||
if let Some(alias) = alias {
|
||||
w!(self, " as {}", alias);
|
||||
}
|
||||
}
|
||||
UseTreeKind::Glob { path } => {
|
||||
if let Some(path) = path {
|
||||
w!(self, "{}::", path.display(self.db));
|
||||
w!(self, "{}::", path.display(self.db.upcast()));
|
||||
}
|
||||
w!(self, "*");
|
||||
}
|
||||
UseTreeKind::Prefixed { prefix, list } => {
|
||||
if let Some(prefix) = prefix {
|
||||
w!(self, "{}::", prefix.display(self.db));
|
||||
w!(self, "{}::", prefix.display(self.db.upcast()));
|
||||
}
|
||||
w!(self, "{{");
|
||||
for (i, tree) in list.iter().enumerate() {
|
||||
|
@ -209,7 +207,7 @@ impl Printer<'_> {
|
|||
ModItem::ExternCrate(it) => {
|
||||
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "extern crate {}", name.display(self.db));
|
||||
w!(self, "extern crate {}", name.display(self.db.upcast()));
|
||||
if let Some(alias) = alias {
|
||||
w!(self, " as {}", alias);
|
||||
}
|
||||
|
@ -256,7 +254,7 @@ impl Printer<'_> {
|
|||
if let Some(abi) = abi {
|
||||
w!(self, "extern \"{}\" ", abi);
|
||||
}
|
||||
w!(self, "fn {}", name.display(self.db));
|
||||
w!(self, "fn {}", name.display(self.db.upcast()));
|
||||
self.print_generic_params(explicit_generic_params);
|
||||
w!(self, "(");
|
||||
if !params.is_empty() {
|
||||
|
@ -290,7 +288,7 @@ impl Printer<'_> {
|
|||
ModItem::Struct(it) => {
|
||||
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "struct {}", name.display(self.db));
|
||||
w!(self, "struct {}", name.display(self.db.upcast()));
|
||||
self.print_generic_params(generic_params);
|
||||
self.print_fields_and_where_clause(fields, generic_params);
|
||||
if matches!(fields, Fields::Record(_)) {
|
||||
|
@ -302,7 +300,7 @@ impl Printer<'_> {
|
|||
ModItem::Union(it) => {
|
||||
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "union {}", name.display(self.db));
|
||||
w!(self, "union {}", name.display(self.db.upcast()));
|
||||
self.print_generic_params(generic_params);
|
||||
self.print_fields_and_where_clause(fields, generic_params);
|
||||
if matches!(fields, Fields::Record(_)) {
|
||||
|
@ -314,14 +312,14 @@ impl Printer<'_> {
|
|||
ModItem::Enum(it) => {
|
||||
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "enum {}", name.display(self.db));
|
||||
w!(self, "enum {}", name.display(self.db.upcast()));
|
||||
self.print_generic_params(generic_params);
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
self.indented(|this| {
|
||||
for variant in variants.clone() {
|
||||
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
|
||||
this.print_attrs_of(variant, "\n");
|
||||
w!(this, "{}", name.display(self.db));
|
||||
w!(this, "{}", name.display(self.db.upcast()));
|
||||
this.print_fields(fields);
|
||||
wln!(this, ",");
|
||||
}
|
||||
|
@ -333,7 +331,7 @@ impl Printer<'_> {
|
|||
self.print_visibility(*visibility);
|
||||
w!(self, "const ");
|
||||
match name {
|
||||
Some(name) => w!(self, "{}", name.display(self.db)),
|
||||
Some(name) => w!(self, "{}", name.display(self.db.upcast())),
|
||||
None => w!(self, "_"),
|
||||
}
|
||||
w!(self, ": ");
|
||||
|
@ -347,7 +345,7 @@ impl Printer<'_> {
|
|||
if *mutable {
|
||||
w!(self, "mut ");
|
||||
}
|
||||
w!(self, "{}: ", name.display(self.db));
|
||||
w!(self, "{}: ", name.display(self.db.upcast()));
|
||||
self.print_type_ref(type_ref);
|
||||
w!(self, " = _;");
|
||||
wln!(self);
|
||||
|
@ -369,7 +367,7 @@ impl Printer<'_> {
|
|||
if *is_auto {
|
||||
w!(self, "auto ");
|
||||
}
|
||||
w!(self, "trait {}", name.display(self.db));
|
||||
w!(self, "trait {}", name.display(self.db.upcast()));
|
||||
self.print_generic_params(generic_params);
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
self.indented(|this| {
|
||||
|
@ -382,7 +380,7 @@ impl Printer<'_> {
|
|||
ModItem::TraitAlias(it) => {
|
||||
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "trait {}", name.display(self.db));
|
||||
w!(self, "trait {}", name.display(self.db.upcast()));
|
||||
self.print_generic_params(generic_params);
|
||||
w!(self, " = ");
|
||||
self.print_where_clause(generic_params);
|
||||
|
@ -415,7 +413,7 @@ impl Printer<'_> {
|
|||
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
|
||||
&self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "type {}", name.display(self.db));
|
||||
w!(self, "type {}", name.display(self.db.upcast()));
|
||||
self.print_generic_params(generic_params);
|
||||
if !bounds.is_empty() {
|
||||
w!(self, ": ");
|
||||
|
@ -432,7 +430,7 @@ impl Printer<'_> {
|
|||
ModItem::Mod(it) => {
|
||||
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "mod {}", name.display(self.db));
|
||||
w!(self, "mod {}", name.display(self.db.upcast()));
|
||||
match kind {
|
||||
ModKind::Inline { items } => {
|
||||
w!(self, " {{");
|
||||
|
@ -450,16 +448,16 @@ impl Printer<'_> {
|
|||
}
|
||||
ModItem::MacroCall(it) => {
|
||||
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
|
||||
wln!(self, "{}!(...);", path.display(self.db));
|
||||
wln!(self, "{}!(...);", path.display(self.db.upcast()));
|
||||
}
|
||||
ModItem::MacroRules(it) => {
|
||||
let MacroRules { name, ast_id: _ } = &self.tree[it];
|
||||
wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db));
|
||||
wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db.upcast()));
|
||||
}
|
||||
ModItem::MacroDef(it) => {
|
||||
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
wln!(self, "macro {} {{ ... }}", name.display(self.db));
|
||||
wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,7 +489,7 @@ impl Printer<'_> {
|
|||
}
|
||||
first = false;
|
||||
self.print_attrs_of(idx, " ");
|
||||
w!(self, "{}", lt.name.display(self.db));
|
||||
w!(self, "{}", lt.name.display(self.db.upcast()));
|
||||
}
|
||||
for (idx, x) in params.type_or_consts.iter() {
|
||||
if !first {
|
||||
|
@ -501,11 +499,11 @@ impl Printer<'_> {
|
|||
self.print_attrs_of(idx, " ");
|
||||
match x {
|
||||
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
|
||||
Some(name) => w!(self, "{}", name.display(self.db)),
|
||||
Some(name) => w!(self, "{}", name.display(self.db.upcast())),
|
||||
None => w!(self, "_anon_{}", idx.into_raw()),
|
||||
},
|
||||
TypeOrConstParamData::ConstParamData(konst) => {
|
||||
w!(self, "const {}: ", konst.name.display(self.db));
|
||||
w!(self, "const {}: ", konst.name.display(self.db.upcast()));
|
||||
self.print_type_ref(&konst.ty);
|
||||
}
|
||||
}
|
||||
|
@ -540,8 +538,8 @@ impl Printer<'_> {
|
|||
wln!(
|
||||
this,
|
||||
"{}: {},",
|
||||
target.name.display(self.db),
|
||||
bound.name.display(self.db)
|
||||
target.name.display(self.db.upcast()),
|
||||
bound.name.display(self.db.upcast())
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -551,7 +549,7 @@ impl Printer<'_> {
|
|||
if i != 0 {
|
||||
w!(this, ", ");
|
||||
}
|
||||
w!(this, "{}", lt.display(self.db));
|
||||
w!(this, "{}", lt.display(self.db.upcast()));
|
||||
}
|
||||
w!(this, "> ");
|
||||
(target, bound)
|
||||
|
@ -562,7 +560,7 @@ impl Printer<'_> {
|
|||
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
|
||||
WherePredicateTypeTarget::TypeOrConstParam(id) => {
|
||||
match ¶ms.type_or_consts[*id].name() {
|
||||
Some(name) => w!(this, "{}", name.display(self.db)),
|
||||
Some(name) => w!(this, "{}", name.display(self.db.upcast())),
|
||||
None => w!(this, "_anon_{}", id.into_raw()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//!
|
||||
//! This attribute to tell the compiler about semi built-in std library
|
||||
//! features, such as Fn family of traits.
|
||||
use hir_expand::name::Name;
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::SmolStr;
|
||||
use triomphe::Arc;
|
||||
|
@ -238,7 +239,17 @@ impl LangItem {
|
|||
|
||||
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
|
||||
let t = db.lang_item(start_crate, *self)?;
|
||||
Some(Path::LangItem(t))
|
||||
Some(Path::LangItem(t, None))
|
||||
}
|
||||
|
||||
pub fn ty_rel_path(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
start_crate: CrateId,
|
||||
seg: Name,
|
||||
) -> Option<Path> {
|
||||
let t = db.lang_item(start_crate, *self)?;
|
||||
Some(Path::LangItem(t, Some(seg)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
//! Context for lowering paths.
|
||||
use std::cell::OnceCell;
|
||||
|
||||
use hir_expand::{
|
||||
ast_id_map::{AstIdMap, AstIdNode},
|
||||
hygiene::Hygiene,
|
||||
AstId, HirFileId, InFile,
|
||||
};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
|
|
|
@ -22,6 +22,45 @@ fn main() { 0 as u32; }
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_asm_expand() {
|
||||
check(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! asm {() => {}}
|
||||
|
||||
fn main() {
|
||||
let i: u64 = 3;
|
||||
let o: u64;
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov {0}, {1}",
|
||||
"add {0}, 5",
|
||||
out(reg) o,
|
||||
in(reg) i,
|
||||
);
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! asm {() => {}}
|
||||
|
||||
fn main() {
|
||||
let i: u64 = 3;
|
||||
let o: u64;
|
||||
unsafe {
|
||||
builtin #asm ( {
|
||||
$crate::format_args!("mov {0}, {1}");
|
||||
$crate::format_args!("add {0}, 5");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_expand() {
|
||||
check(
|
||||
|
@ -201,7 +240,7 @@ macro_rules! format_args {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(arg2), ::core::fmt::Debug::fmt), ]);
|
||||
builtin #format_args ("{} {:?}", arg1(a, b, c), arg2);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
@ -219,10 +258,10 @@ macro_rules! format_args {
|
|||
|
||||
fn main() {
|
||||
format_args!(x = 2);
|
||||
format_args!(x =);
|
||||
format_args!(x =, x = 2);
|
||||
format_args!("{}", x =);
|
||||
format_args!(=, "{}", x =);
|
||||
format_args!/*+errors*/(x =);
|
||||
format_args!/*+errors*/(x =, x = 2);
|
||||
format_args!/*+errors*/("{}", x =);
|
||||
format_args!/*+errors*/(=, "{}", x =);
|
||||
format_args!(x = 2, "{}", 5);
|
||||
}
|
||||
"#,
|
||||
|
@ -234,12 +273,19 @@ macro_rules! format_args {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
/* error: no rule matches input tokens */;
|
||||
/* error: expected expression */;
|
||||
/* error: expected expression, expected COMMA */;
|
||||
/* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]);
|
||||
/* error: expected expression, expected R_PAREN */;
|
||||
::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]);
|
||||
builtin #format_args (x = 2);
|
||||
/* parse error: expected expression */
|
||||
builtin #format_args (x = );
|
||||
/* parse error: expected expression */
|
||||
/* parse error: expected R_PAREN */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
builtin #format_args (x = , x = 2);
|
||||
/* parse error: expected expression */
|
||||
builtin #format_args ("{}", x = );
|
||||
/* parse error: expected expression */
|
||||
/* parse error: expected expression */
|
||||
builtin #format_args ( = , "{}", x = );
|
||||
builtin #format_args (x = 2, "{}", 5);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
@ -267,7 +313,7 @@ macro_rules! format_args {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]);
|
||||
builtin #format_args ("{} {:?}", a::<A, B>(), b);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
@ -300,7 +346,7 @@ macro_rules! format_args {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]);
|
||||
builtin #format_args (r#"{},mismatch,"{}","{}""#, location_csv_pat(db, &analysis, vfs, &sm, pat_id), mismatch.expected.display(db), mismatch.actual.display(db));
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
@ -334,7 +380,7 @@ macro_rules! format_args {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::ArgumentV1::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]);
|
||||
builtin #format_args (concat!("xxx{}y", "{:?}zzz"), 2, b);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
@ -364,8 +410,8 @@ macro_rules! format_args {
|
|||
|
||||
fn main() {
|
||||
let _ =
|
||||
/* error: expected field name or number *//* parse error: expected field name or number */
|
||||
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(), ::core::fmt::Debug::fmt), ]);
|
||||
/* parse error: expected field name or number */
|
||||
builtin #format_args ("{} {:?}", a.);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
|
|
@ -117,7 +117,7 @@ fn main(foo: ()) {
|
|||
macro_rules! format_args {}
|
||||
|
||||
fn main(foo: ()) {
|
||||
/* error: unresolved macro identity */::core::fmt::Arguments::new_v1(&["", " ", " ", ], &[::core::fmt::ArgumentV1::new(&(::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(0), ::core::fmt::Display::fmt), ])), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(foo), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(identity!(10)), ::core::fmt::Display::fmt), ])
|
||||
builtin #format_args ("{} {} {}", format_args!("{}", 0), foo, identity!(10), "bar")
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
@ -150,8 +150,8 @@ macro_rules! identity {
|
|||
}
|
||||
|
||||
fn main(foo: ()) {
|
||||
// format_args/*+tokenids*/!("{} {} {}"#1,#3 format_args!("{}", 0#10),#12 foo#13,#14 identity!(10#18),#21 "bar"#22)
|
||||
::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(�[#4294967295""#4294967295,#4294967295 " "#4294967295,#4294967295 " "#4294967295,#4294967295 ]#4294967295,#4294967295 �[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(�[#4294967295""#4294967295,#4294967295 ]#4294967295,#4294967295 �[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#42949672950#10)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#4294967295foo#13)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#429496729510#18)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295
|
||||
// format_args/*+tokenids*/!("{} {} {}"#1,#2 format_args#3!#4("{}"#6,#7 0#8),#9 foo#10,#11 identity#12!#13(10#15),#16 "bar"#17)
|
||||
builtin#4294967295 ##4294967295format_args#4294967295 (#0"{} {} {}"#1,#2 format_args#3!#4(#5"{}"#6,#7 0#8)#5,#9 foo#10,#11 identity#12!#13(#1410#15)#14,#16 "bar"#17)#0
|
||||
}
|
||||
|
||||
"##]],
|
||||
|
|
|
@ -929,8 +929,8 @@ fn main() {
|
|||
macro_rules! format_args {}
|
||||
|
||||
fn main() {
|
||||
/* error: expected field name or number *//* parse error: expected field name or number */
|
||||
::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(line.1.), ::core::fmt::Display::fmt), ]);
|
||||
/* parse error: expected field name or number */
|
||||
builtin #format_args ("{}", line.1.);
|
||||
}
|
||||
|
||||
"##]],
|
||||
|
@ -956,19 +956,15 @@ fn main() {
|
|||
macro_rules! format_args {}
|
||||
|
||||
fn main() {
|
||||
/* error: expected COMMA, expected R_BRACK, expected COMMA, expected COMMA, expected expression, expected R_PAREN *//* parse error: expected COMMA */
|
||||
/* parse error: expected COMMA */
|
||||
/* parse error: expected R_BRACK */
|
||||
/* parse error: expected COMMA */
|
||||
/* parse error: expected COMMA */
|
||||
/* parse error: expected expression */
|
||||
/* parse error: expected R_PAREN */
|
||||
/* parse error: expected R_PAREN */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(&[0 2]), ::core::fmt::Display::fmt), ]);
|
||||
builtin #format_args ("{}", &[0 2]);
|
||||
}
|
||||
|
||||
"##]],
|
||||
|
|
|
@ -93,7 +93,7 @@ use crate::{
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct DefMap {
|
||||
_c: Count<Self>,
|
||||
/// When this is a block def map, this will hold the block id of the the block and module that
|
||||
/// When this is a block def map, this will hold the block id of the block and module that
|
||||
/// contains this block.
|
||||
block: Option<BlockInfo>,
|
||||
/// The modules and their data declared in this crate.
|
||||
|
|
|
@ -1828,7 +1828,11 @@ impl ModCollector<'_, '_> {
|
|||
let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
|
||||
// `#[macro_use]` (without any paths) found, forget collected names and just import
|
||||
// all visible macros.
|
||||
self.def_collector.import_macros_from_extern_crate(target_crate, None, Some(extern_crate_id));
|
||||
self.def_collector.import_macros_from_extern_crate(
|
||||
target_crate,
|
||||
None,
|
||||
Some(extern_crate_id),
|
||||
);
|
||||
return;
|
||||
};
|
||||
for path in paths {
|
||||
|
|
|
@ -47,7 +47,7 @@ pub enum Path {
|
|||
},
|
||||
/// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
|
||||
/// links via a normal path since they might be private and not accessible in the usage place.
|
||||
LangItem(LangItemTarget),
|
||||
LangItem(LangItemTarget, Option<Name>),
|
||||
}
|
||||
|
||||
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
|
||||
|
@ -122,33 +122,40 @@ impl Path {
|
|||
pub fn kind(&self) -> &PathKind {
|
||||
match self {
|
||||
Path::Normal { mod_path, .. } => &mod_path.kind,
|
||||
Path::LangItem(_) => &PathKind::Abs,
|
||||
Path::LangItem(..) => &PathKind::Abs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_anchor(&self) -> Option<&TypeRef> {
|
||||
match self {
|
||||
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
|
||||
Path::LangItem(_) => None,
|
||||
Path::LangItem(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn segments(&self) -> PathSegments<'_> {
|
||||
let Path::Normal { mod_path, generic_args, .. } = self else {
|
||||
return PathSegments { segments: &[], generic_args: None };
|
||||
};
|
||||
let s =
|
||||
PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
|
||||
if let Some(generic_args) = s.generic_args {
|
||||
assert_eq!(s.segments.len(), generic_args.len());
|
||||
match self {
|
||||
Path::Normal { mod_path, generic_args, .. } => {
|
||||
let s = PathSegments {
|
||||
segments: mod_path.segments(),
|
||||
generic_args: generic_args.as_deref(),
|
||||
};
|
||||
if let Some(generic_args) = s.generic_args {
|
||||
assert_eq!(s.segments.len(), generic_args.len());
|
||||
}
|
||||
s
|
||||
}
|
||||
Path::LangItem(_, seg) => PathSegments {
|
||||
segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
|
||||
generic_args: None,
|
||||
},
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
pub fn mod_path(&self) -> Option<&ModPath> {
|
||||
match self {
|
||||
Path::Normal { mod_path, .. } => Some(&mod_path),
|
||||
Path::LangItem(_) => None,
|
||||
Path::LangItem(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,18 +2,54 @@
|
|||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
|
||||
use hir_expand::mod_path::PathKind;
|
||||
use intern::Interned;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
lang_item::LangItemTarget,
|
||||
path::{GenericArg, GenericArgs, Path},
|
||||
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
|
||||
};
|
||||
|
||||
pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
||||
if let Path::LangItem(it) = path {
|
||||
return write!(buf, "$lang_item::{it:?}");
|
||||
pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
||||
if let Path::LangItem(it, s) = path {
|
||||
write!(buf, "builtin#lang(")?;
|
||||
match *it {
|
||||
LangItemTarget::ImplDef(it) => write!(buf, "{it:?}")?,
|
||||
LangItemTarget::EnumId(it) => {
|
||||
write!(buf, "{}", db.enum_data(it).name.display(db.upcast()))?
|
||||
}
|
||||
LangItemTarget::Function(it) => {
|
||||
write!(buf, "{}", db.function_data(it).name.display(db.upcast()))?
|
||||
}
|
||||
LangItemTarget::Static(it) => {
|
||||
write!(buf, "{}", db.static_data(it).name.display(db.upcast()))?
|
||||
}
|
||||
LangItemTarget::Struct(it) => {
|
||||
write!(buf, "{}", db.struct_data(it).name.display(db.upcast()))?
|
||||
}
|
||||
LangItemTarget::Union(it) => {
|
||||
write!(buf, "{}", db.union_data(it).name.display(db.upcast()))?
|
||||
}
|
||||
LangItemTarget::TypeAlias(it) => {
|
||||
write!(buf, "{}", db.type_alias_data(it).name.display(db.upcast()))?
|
||||
}
|
||||
LangItemTarget::Trait(it) => {
|
||||
write!(buf, "{}", db.trait_data(it).name.display(db.upcast()))?
|
||||
}
|
||||
LangItemTarget::EnumVariant(it) => write!(
|
||||
buf,
|
||||
"{}",
|
||||
db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast())
|
||||
)?,
|
||||
}
|
||||
|
||||
if let Some(s) = s {
|
||||
write!(buf, "::{}", s.display(db.upcast()))?;
|
||||
}
|
||||
return write!(buf, ")");
|
||||
}
|
||||
match path.type_anchor() {
|
||||
Some(anchor) => {
|
||||
|
@ -44,7 +80,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri
|
|||
write!(buf, "::")?;
|
||||
}
|
||||
|
||||
write!(buf, "{}", segment.name.display(db))?;
|
||||
write!(buf, "{}", segment.name.display(db.upcast()))?;
|
||||
if let Some(generics) = segment.args_and_bindings {
|
||||
write!(buf, "::<")?;
|
||||
print_generic_args(db, generics, buf)?;
|
||||
|
@ -57,7 +93,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri
|
|||
}
|
||||
|
||||
pub(crate) fn print_generic_args(
|
||||
db: &dyn ExpandDatabase,
|
||||
db: &dyn DefDatabase,
|
||||
generics: &GenericArgs,
|
||||
buf: &mut dyn Write,
|
||||
) -> fmt::Result {
|
||||
|
@ -83,7 +119,7 @@ pub(crate) fn print_generic_args(
|
|||
write!(buf, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
write!(buf, "{}", binding.name.display(db))?;
|
||||
write!(buf, "{}", binding.name.display(db.upcast()))?;
|
||||
if !binding.bounds.is_empty() {
|
||||
write!(buf, ": ")?;
|
||||
print_type_bounds(db, &binding.bounds, buf)?;
|
||||
|
@ -97,19 +133,19 @@ pub(crate) fn print_generic_args(
|
|||
}
|
||||
|
||||
pub(crate) fn print_generic_arg(
|
||||
db: &dyn ExpandDatabase,
|
||||
db: &dyn DefDatabase,
|
||||
arg: &GenericArg,
|
||||
buf: &mut dyn Write,
|
||||
) -> fmt::Result {
|
||||
match arg {
|
||||
GenericArg::Type(ty) => print_type_ref(db, ty, buf),
|
||||
GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
|
||||
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
|
||||
GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast())),
|
||||
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast())),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_type_ref(
|
||||
db: &dyn ExpandDatabase,
|
||||
db: &dyn DefDatabase,
|
||||
type_ref: &TypeRef,
|
||||
buf: &mut dyn Write,
|
||||
) -> fmt::Result {
|
||||
|
@ -143,7 +179,7 @@ pub(crate) fn print_type_ref(
|
|||
};
|
||||
write!(buf, "&")?;
|
||||
if let Some(lt) = lt {
|
||||
write!(buf, "{} ", lt.name.display(db))?;
|
||||
write!(buf, "{} ", lt.name.display(db.upcast()))?;
|
||||
}
|
||||
write!(buf, "{mtbl}")?;
|
||||
print_type_ref(db, pointee, buf)?;
|
||||
|
@ -151,7 +187,7 @@ pub(crate) fn print_type_ref(
|
|||
TypeRef::Array(elem, len) => {
|
||||
write!(buf, "[")?;
|
||||
print_type_ref(db, elem, buf)?;
|
||||
write!(buf, "; {}]", len.display(db))?;
|
||||
write!(buf, "; {}]", len.display(db.upcast()))?;
|
||||
}
|
||||
TypeRef::Slice(elem) => {
|
||||
write!(buf, "[")?;
|
||||
|
@ -198,7 +234,7 @@ pub(crate) fn print_type_ref(
|
|||
}
|
||||
|
||||
pub(crate) fn print_type_bounds(
|
||||
db: &dyn ExpandDatabase,
|
||||
db: &dyn DefDatabase,
|
||||
bounds: &[Interned<TypeBound>],
|
||||
buf: &mut dyn Write,
|
||||
) -> fmt::Result {
|
||||
|
@ -216,10 +252,14 @@ pub(crate) fn print_type_bounds(
|
|||
print_path(db, path, buf)?;
|
||||
}
|
||||
TypeBound::ForLifetime(lifetimes, path) => {
|
||||
write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
|
||||
write!(
|
||||
buf,
|
||||
"for<{}> ",
|
||||
lifetimes.iter().map(|it| it.display(db.upcast())).format(", ")
|
||||
)?;
|
||||
print_path(db, path, buf)?;
|
||||
}
|
||||
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
|
||||
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast()))?,
|
||||
TypeBound::Error => write!(buf, "{{unknown}}")?,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,22 +156,19 @@ impl Resolver {
|
|||
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
|
||||
let path = match path {
|
||||
Path::Normal { mod_path, .. } => mod_path,
|
||||
Path::LangItem(l) => {
|
||||
return Some((
|
||||
match *l {
|
||||
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
|
||||
LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
|
||||
LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
|
||||
LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
|
||||
LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
|
||||
LangItemTarget::Trait(it) => TypeNs::TraitId(it),
|
||||
LangItemTarget::Function(_)
|
||||
| LangItemTarget::ImplDef(_)
|
||||
| LangItemTarget::Static(_) => return None,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
))
|
||||
Path::LangItem(l, seg) => {
|
||||
let type_ns = match *l {
|
||||
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
|
||||
LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
|
||||
LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
|
||||
LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
|
||||
LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
|
||||
LangItemTarget::Trait(it) => TypeNs::TraitId(it),
|
||||
LangItemTarget::Function(_)
|
||||
| LangItemTarget::ImplDef(_)
|
||||
| LangItemTarget::Static(_) => return None,
|
||||
};
|
||||
return Some((type_ns, seg.as_ref().map(|_| 1), None));
|
||||
}
|
||||
};
|
||||
let first_name = path.segments().first()?;
|
||||
|
@ -256,7 +253,7 @@ impl Resolver {
|
|||
) -> Option<ResolveValueResult> {
|
||||
let path = match path {
|
||||
Path::Normal { mod_path, .. } => mod_path,
|
||||
Path::LangItem(l) => {
|
||||
Path::LangItem(l, None) => {
|
||||
return Some(ResolveValueResult::ValueNs(
|
||||
match *l {
|
||||
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
|
||||
|
@ -272,6 +269,20 @@ impl Resolver {
|
|||
None,
|
||||
))
|
||||
}
|
||||
Path::LangItem(l, Some(_)) => {
|
||||
let type_ns = match *l {
|
||||
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
|
||||
LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
|
||||
LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
|
||||
LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
|
||||
LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
|
||||
LangItemTarget::Trait(it) => TypeNs::TraitId(it),
|
||||
LangItemTarget::Function(_)
|
||||
| LangItemTarget::ImplDef(_)
|
||||
| LangItemTarget::Static(_) => return None,
|
||||
};
|
||||
return Some(ResolveValueResult::Partial(type_ns, 1, None));
|
||||
}
|
||||
};
|
||||
let n_segments = path.segments().len();
|
||||
let tmp = name![self];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue