mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-29 06:23:25 +00:00
Merge commit '5ecace48f693afaa6adf8cb23086b651db3aec96' into sync-from-ra
This commit is contained in:
parent
ce642071d8
commit
5a95a53a39
153 changed files with 3382 additions and 1925 deletions
9
.github/workflows/release.yaml
vendored
9
.github/workflows/release.yaml
vendored
|
@ -36,7 +36,6 @@ jobs:
|
|||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
code-target: linux-x64
|
||||
container: ubuntu:18.04
|
||||
- os: ubuntu-20.04
|
||||
target: aarch64-unknown-linux-gnu
|
||||
code-target: linux-arm64
|
||||
|
@ -63,14 +62,6 @@ jobs:
|
|||
with:
|
||||
fetch-depth: ${{ env.FETCH_DEPTH }}
|
||||
|
||||
- name: Install toolchain dependencies
|
||||
if: matrix.container == 'ubuntu:18.04'
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update && apt-get install -y build-essential curl
|
||||
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y
|
||||
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update stable
|
||||
|
|
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -71,6 +71,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"cfg",
|
||||
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lz4_flex",
|
||||
"rustc-hash",
|
||||
"salsa",
|
||||
"semver",
|
||||
|
@ -134,9 +135,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.89"
|
||||
version = "1.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
|
||||
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||
|
||||
[[package]]
|
||||
name = "cfg"
|
||||
|
@ -874,9 +875,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
|||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.4",
|
||||
|
@ -992,6 +993,12 @@ dependencies = [
|
|||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lz4_flex"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15"
|
||||
|
||||
[[package]]
|
||||
name = "mbe"
|
||||
version = "0.0.0"
|
||||
|
@ -1597,6 +1604,7 @@ dependencies = [
|
|||
"rayon",
|
||||
"rustc-hash",
|
||||
"scip",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sourcegen",
|
||||
|
|
|
@ -105,6 +105,10 @@ anyhow = "1.0.75"
|
|||
arrayvec = "0.7.4"
|
||||
bitflags = "2.4.1"
|
||||
cargo_metadata = "0.18.1"
|
||||
chalk-solve = { version = "0.96.0", default-features = false }
|
||||
chalk-ir = "0.96.0"
|
||||
chalk-recursive = { version = "0.96.0", default-features = false }
|
||||
chalk-derive = "0.96.0"
|
||||
command-group = "2.0.1"
|
||||
crossbeam-channel = "0.5.8"
|
||||
dissimilar = "1.0.7"
|
||||
|
|
|
@ -12,6 +12,8 @@ rust-version.workspace = true
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lz4_flex = { version = "0.11", default-features = false }
|
||||
|
||||
la-arena.workspace = true
|
||||
salsa.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
|
|
|
@ -7,13 +7,13 @@ use salsa::Durability;
|
|||
use triomphe::Arc;
|
||||
use vfs::FileId;
|
||||
|
||||
use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId};
|
||||
use crate::{CrateGraph, SourceDatabaseExt, SourceDatabaseExt2, SourceRoot, SourceRootId};
|
||||
|
||||
/// Encapsulate a bunch of raw `.set` calls on the database.
|
||||
#[derive(Default)]
|
||||
pub struct FileChange {
|
||||
pub roots: Option<Vec<SourceRoot>>,
|
||||
pub files_changed: Vec<(FileId, Option<Arc<str>>)>,
|
||||
pub files_changed: Vec<(FileId, Option<String>)>,
|
||||
pub crate_graph: Option<CrateGraph>,
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ impl FileChange {
|
|||
self.roots = Some(roots);
|
||||
}
|
||||
|
||||
pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<str>>) {
|
||||
pub fn change_file(&mut self, file_id: FileId, new_text: Option<String>) {
|
||||
self.files_changed.push((file_id, new_text))
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,8 @@ impl FileChange {
|
|||
let source_root = db.source_root(source_root_id);
|
||||
let durability = durability(&source_root);
|
||||
// XXX: can't actually remove the file, just reset the text
|
||||
let text = text.unwrap_or_else(|| Arc::from(""));
|
||||
db.set_file_text_with_durability(file_id, text, durability)
|
||||
let text = text.unwrap_or_default();
|
||||
db.set_file_text_with_durability(file_id, &text, durability)
|
||||
}
|
||||
if let Some(crate_graph) = self.crate_graph {
|
||||
db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH);
|
||||
|
|
|
@ -7,6 +7,7 @@ mod input;
|
|||
|
||||
use std::panic;
|
||||
|
||||
use salsa::Durability;
|
||||
use syntax::{ast, Parse, SourceFile};
|
||||
use triomphe::Arc;
|
||||
|
||||
|
@ -42,6 +43,7 @@ pub trait Upcast<T: ?Sized> {
|
|||
fn upcast(&self) -> &T;
|
||||
}
|
||||
|
||||
pub const DEFAULT_FILE_TEXT_LRU_CAP: usize = 16;
|
||||
pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
|
||||
pub const DEFAULT_BORROWCK_LRU_CAP: usize = 1024;
|
||||
|
||||
|
@ -89,7 +91,10 @@ fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
|
|||
#[salsa::query_group(SourceDatabaseExtStorage)]
|
||||
pub trait SourceDatabaseExt: SourceDatabase {
|
||||
#[salsa::input]
|
||||
fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>;
|
||||
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str>;
|
||||
|
||||
/// Path to a file, relative to the root of its source root.
|
||||
/// Source root of the file.
|
||||
#[salsa::input]
|
||||
|
@ -101,6 +106,44 @@ pub trait SourceDatabaseExt: SourceDatabase {
|
|||
fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>;
|
||||
}
|
||||
|
||||
fn file_text(db: &dyn SourceDatabaseExt, file_id: FileId) -> Arc<str> {
|
||||
let bytes = db.compressed_file_text(file_id);
|
||||
let bytes =
|
||||
lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail");
|
||||
let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8");
|
||||
Arc::from(text)
|
||||
}
|
||||
|
||||
pub trait SourceDatabaseExt2 {
|
||||
fn set_file_text(&mut self, file_id: FileId, text: &str) {
|
||||
self.set_file_text_with_durability(file_id, text, Durability::LOW);
|
||||
}
|
||||
|
||||
fn set_file_text_with_durability(
|
||||
&mut self,
|
||||
file_id: FileId,
|
||||
text: &str,
|
||||
durability: Durability,
|
||||
);
|
||||
}
|
||||
|
||||
impl<Db: ?Sized + SourceDatabaseExt> SourceDatabaseExt2 for Db {
|
||||
fn set_file_text_with_durability(
|
||||
&mut self,
|
||||
file_id: FileId,
|
||||
text: &str,
|
||||
durability: Durability,
|
||||
) {
|
||||
let bytes = text.as_bytes();
|
||||
let compressed = lz4_flex::compress_prepend_size(bytes);
|
||||
self.set_compressed_file_text_with_durability(
|
||||
file_id,
|
||||
Arc::from(compressed.as_slice()),
|
||||
durability,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[CrateId]> {
|
||||
let graph = db.crate_graph();
|
||||
let mut crates = graph
|
||||
|
|
|
@ -47,6 +47,7 @@ impl CfgExpr {
|
|||
pub fn parse<S>(tt: &tt::Subtree<S>) -> CfgExpr {
|
||||
next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
|
||||
}
|
||||
|
||||
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
|
||||
pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
|
||||
match self {
|
||||
|
@ -62,7 +63,6 @@ impl CfgExpr {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr> {
|
||||
let name = match it.next() {
|
||||
None => return None,
|
||||
|
|
|
@ -28,19 +28,20 @@ pub enum CargoTestMessage {
|
|||
},
|
||||
Suite,
|
||||
Finished,
|
||||
Custom {
|
||||
text: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl ParseFromLine for CargoTestMessage {
|
||||
fn from_line(line: &str, error: &mut String) -> Option<Self> {
|
||||
fn from_line(line: &str, _: &mut String) -> Option<Self> {
|
||||
let mut deserializer = serde_json::Deserializer::from_str(line);
|
||||
deserializer.disable_recursion_limit();
|
||||
if let Ok(message) = CargoTestMessage::deserialize(&mut deserializer) {
|
||||
return Some(message);
|
||||
}
|
||||
|
||||
error.push_str(line);
|
||||
error.push('\n');
|
||||
None
|
||||
Some(CargoTestMessage::Custom { text: line.to_owned() })
|
||||
}
|
||||
|
||||
fn from_eof() -> Option<Self> {
|
||||
|
|
|
@ -10,7 +10,6 @@ use std::ops::Index;
|
|||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{name::Name, HirFileId, InFile};
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -45,7 +44,8 @@ pub struct Body {
|
|||
///
|
||||
/// If this `Body` is for the body of a constant, this will just be
|
||||
/// empty.
|
||||
pub params: Vec<PatId>,
|
||||
pub params: Box<[PatId]>,
|
||||
pub self_param: Option<BindingId>,
|
||||
/// The `ExprId` of the actual body expression.
|
||||
pub body_expr: ExprId,
|
||||
/// Block expressions in this body that may contain inner items.
|
||||
|
@ -55,7 +55,7 @@ pub struct Body {
|
|||
pub type ExprPtr = AstPtr<ast::Expr>;
|
||||
pub type ExprSource = InFile<ExprPtr>;
|
||||
|
||||
pub type PatPtr = AstPtr<Either<ast::Pat, ast::SelfParam>>;
|
||||
pub type PatPtr = AstPtr<ast::Pat>;
|
||||
pub type PatSource = InFile<PatPtr>;
|
||||
|
||||
pub type LabelPtr = AstPtr<ast::Label>;
|
||||
|
@ -63,6 +63,7 @@ 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>;
|
||||
|
||||
|
@ -88,6 +89,8 @@ pub struct BodySourceMap {
|
|||
label_map: FxHashMap<LabelSource, LabelId>,
|
||||
label_map_back: ArenaMap<LabelId, LabelSource>,
|
||||
|
||||
self_param: Option<InFile<AstPtr<ast::SelfParam>>>,
|
||||
|
||||
/// 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_back: FxHashMap<ExprId, FieldSource>,
|
||||
|
@ -215,10 +218,11 @@ impl Body {
|
|||
fn shrink_to_fit(&mut self) {
|
||||
let Self {
|
||||
body_expr: _,
|
||||
params: _,
|
||||
self_param: _,
|
||||
block_scopes,
|
||||
exprs,
|
||||
labels,
|
||||
params,
|
||||
pats,
|
||||
bindings,
|
||||
binding_owners,
|
||||
|
@ -226,7 +230,6 @@ impl Body {
|
|||
block_scopes.shrink_to_fit();
|
||||
exprs.shrink_to_fit();
|
||||
labels.shrink_to_fit();
|
||||
params.shrink_to_fit();
|
||||
pats.shrink_to_fit();
|
||||
bindings.shrink_to_fit();
|
||||
binding_owners.shrink_to_fit();
|
||||
|
@ -297,6 +300,7 @@ impl Default for Body {
|
|||
params: Default::default(),
|
||||
block_scopes: Default::default(),
|
||||
binding_owners: Default::default(),
|
||||
self_param: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,14 +358,12 @@ impl BodySourceMap {
|
|||
self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
|
||||
}
|
||||
|
||||
pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
|
||||
let src = node.map(|it| AstPtr::new(it).wrap_left());
|
||||
self.pat_map.get(&src).cloned()
|
||||
pub fn self_param_syntax(&self) -> Option<InFile<AstPtr<ast::SelfParam>>> {
|
||||
self.self_param
|
||||
}
|
||||
|
||||
pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> {
|
||||
let src = node.map(|it| AstPtr::new(it).wrap_right());
|
||||
self.pat_map.get(&src).cloned()
|
||||
pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
|
||||
self.pat_map.get(&node.map(AstPtr::new)).cloned()
|
||||
}
|
||||
|
||||
pub fn label_syntax(&self, label: LabelId) -> LabelSource {
|
||||
|
@ -401,6 +403,7 @@ impl BodySourceMap {
|
|||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
let Self {
|
||||
self_param: _,
|
||||
expr_map,
|
||||
expr_map_back,
|
||||
pat_map,
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
use std::mem;
|
||||
|
||||
use base_db::CrateId;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
name::{name, AsName, Name},
|
||||
ExpandError, InFile,
|
||||
|
@ -29,7 +28,6 @@ use crate::{
|
|||
db::DefDatabase,
|
||||
expander::Expander,
|
||||
hir::{
|
||||
dummy_expr_id,
|
||||
format_args::{
|
||||
self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
|
||||
FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
|
||||
|
@ -66,16 +64,7 @@ pub(super) fn lower(
|
|||
def_map: expander.module.def_map(db),
|
||||
source_map: BodySourceMap::default(),
|
||||
ast_id_map: db.ast_id_map(expander.current_file_id()),
|
||||
body: Body {
|
||||
exprs: Default::default(),
|
||||
pats: Default::default(),
|
||||
bindings: Default::default(),
|
||||
binding_owners: Default::default(),
|
||||
labels: Default::default(),
|
||||
params: Vec::new(),
|
||||
body_expr: dummy_expr_id(),
|
||||
block_scopes: Vec::new(),
|
||||
},
|
||||
body: Body::default(),
|
||||
expander,
|
||||
current_try_block_label: None,
|
||||
is_lowering_assignee_expr: false,
|
||||
|
@ -191,35 +180,35 @@ impl ExprCollector<'_> {
|
|||
is_async_fn: bool,
|
||||
) -> (Body, BodySourceMap) {
|
||||
if let Some((param_list, mut attr_enabled)) = param_list {
|
||||
let mut params = vec![];
|
||||
if let Some(self_param) =
|
||||
param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
|
||||
{
|
||||
let is_mutable =
|
||||
self_param.mut_token().is_some() && self_param.amp_token().is_none();
|
||||
let ptr = AstPtr::new(&Either::Right(self_param));
|
||||
let binding_id: la_arena::Idx<Binding> =
|
||||
self.alloc_binding(name![self], BindingAnnotation::new(is_mutable, false));
|
||||
let param_pat = self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, ptr);
|
||||
self.add_definition_to_binding(binding_id, param_pat);
|
||||
self.body.params.push(param_pat);
|
||||
self.body.self_param = Some(binding_id);
|
||||
self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param)));
|
||||
}
|
||||
|
||||
for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
|
||||
{
|
||||
let param_pat = self.collect_pat_top(param.pat());
|
||||
self.body.params.push(param_pat);
|
||||
params.push(param_pat);
|
||||
}
|
||||
self.body.params = params.into_boxed_slice();
|
||||
};
|
||||
self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
|
||||
if is_async_fn {
|
||||
match body {
|
||||
Some(e) => {
|
||||
let syntax_ptr = AstPtr::new(&e);
|
||||
let expr = this.collect_expr(e);
|
||||
this.alloc_expr_desugared(Expr::Async {
|
||||
id: None,
|
||||
statements: Box::new([]),
|
||||
tail: Some(expr),
|
||||
})
|
||||
this.alloc_expr_desugared_with_ptr(
|
||||
Expr::Async { id: None, statements: Box::new([]), tail: Some(expr) },
|
||||
syntax_ptr,
|
||||
)
|
||||
}
|
||||
None => this.missing_expr(),
|
||||
}
|
||||
|
@ -405,7 +394,7 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
ast::Expr::ParenExpr(e) => {
|
||||
let inner = self.collect_expr_opt(e.expr());
|
||||
// make the paren expr point to the inner expression as well
|
||||
// make the paren expr point to the inner expression as well for IDE resolution
|
||||
let src = self.expander.in_file(syntax_ptr);
|
||||
self.source_map.expr_map.insert(src, inner);
|
||||
inner
|
||||
|
@ -707,6 +696,7 @@ impl ExprCollector<'_> {
|
|||
.alloc_label_desugared(Label { name: Name::generate_new_name(self.body.labels.len()) });
|
||||
let old_label = self.current_try_block_label.replace(label);
|
||||
|
||||
let ptr = AstPtr::new(&e).upcast();
|
||||
let (btail, expr_id) = self.with_labeled_rib(label, |this| {
|
||||
let mut btail = None;
|
||||
let block = this.collect_block_(e, |id, statements, tail| {
|
||||
|
@ -716,23 +706,21 @@ impl ExprCollector<'_> {
|
|||
(btail, block)
|
||||
});
|
||||
|
||||
let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
|
||||
let callee = self.alloc_expr_desugared_with_ptr(Expr::Path(try_from_output), ptr);
|
||||
let next_tail = match btail {
|
||||
Some(tail) => self.alloc_expr_desugared(Expr::Call {
|
||||
callee,
|
||||
args: Box::new([tail]),
|
||||
is_assignee_expr: false,
|
||||
}),
|
||||
Some(tail) => self.alloc_expr_desugared_with_ptr(
|
||||
Expr::Call { callee, args: Box::new([tail]), is_assignee_expr: false },
|
||||
ptr,
|
||||
),
|
||||
None => {
|
||||
let unit = self.alloc_expr_desugared(Expr::Tuple {
|
||||
exprs: Box::new([]),
|
||||
is_assignee_expr: false,
|
||||
});
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee,
|
||||
args: Box::new([unit]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
let unit = self.alloc_expr_desugared_with_ptr(
|
||||
Expr::Tuple { exprs: Box::new([]), is_assignee_expr: false },
|
||||
ptr,
|
||||
);
|
||||
self.alloc_expr_desugared_with_ptr(
|
||||
Expr::Call { callee, args: Box::new([unit]), is_assignee_expr: false },
|
||||
ptr,
|
||||
)
|
||||
}
|
||||
};
|
||||
let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
|
||||
|
@ -1067,16 +1055,12 @@ impl ExprCollector<'_> {
|
|||
None => None,
|
||||
},
|
||||
);
|
||||
match expansion {
|
||||
Some(tail) => {
|
||||
// Make the macro-call point to its expanded expression so we can query
|
||||
// semantics on syntax pointers to the macro
|
||||
let src = self.expander.in_file(syntax_ptr);
|
||||
self.source_map.expr_map.insert(src, tail);
|
||||
Some(tail)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
expansion.inspect(|&tail| {
|
||||
// Make the macro-call point to its expanded expression so we can query
|
||||
// semantics on syntax pointers to the macro
|
||||
let src = self.expander.in_file(syntax_ptr);
|
||||
self.source_map.expr_map.insert(src, tail);
|
||||
})
|
||||
}
|
||||
|
||||
fn collect_stmt(&mut self, statements: &mut Vec<Statement>, s: ast::Stmt) {
|
||||
|
@ -1261,7 +1245,7 @@ impl ExprCollector<'_> {
|
|||
(Some(id), Pat::Bind { id, subpat })
|
||||
};
|
||||
|
||||
let ptr = AstPtr::new(&Either::Left(pat));
|
||||
let ptr = AstPtr::new(&pat);
|
||||
let pat = self.alloc_pat(pattern, ptr);
|
||||
if let Some(binding_id) = binding {
|
||||
self.add_definition_to_binding(binding_id, pat);
|
||||
|
@ -1359,9 +1343,10 @@ impl ExprCollector<'_> {
|
|||
suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
|
||||
}
|
||||
}
|
||||
#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
|
||||
ast::Pat::LiteralPat(lit) => 'b: {
|
||||
let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing };
|
||||
let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else {
|
||||
break 'b Pat::Missing;
|
||||
};
|
||||
let expr = Expr::Literal(hir_lit);
|
||||
let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
|
||||
let expr_id = self.alloc_expr(expr, expr_ptr);
|
||||
|
@ -1397,7 +1382,7 @@ impl ExprCollector<'_> {
|
|||
ast::Pat::MacroPat(mac) => match mac.macro_call() {
|
||||
Some(call) => {
|
||||
let macro_ptr = AstPtr::new(&call);
|
||||
let src = self.expander.in_file(AstPtr::new(&Either::Left(pat)));
|
||||
let src = self.expander.in_file(AstPtr::new(&pat));
|
||||
let pat =
|
||||
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
|
||||
this.collect_pat_opt(expanded_pat, binding_list)
|
||||
|
@ -1426,7 +1411,7 @@ impl ExprCollector<'_> {
|
|||
Pat::Range { start, end }
|
||||
}
|
||||
};
|
||||
let ptr = AstPtr::new(&Either::Left(pat));
|
||||
let ptr = AstPtr::new(&pat);
|
||||
self.alloc_pat(pattern, ptr)
|
||||
}
|
||||
|
||||
|
@ -1987,10 +1972,19 @@ impl ExprCollector<'_> {
|
|||
self.source_map.expr_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
|
||||
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
|
||||
// Migrate to alloc_expr_desugared_with_ptr and then rename back
|
||||
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
|
||||
self.body.exprs.alloc(expr)
|
||||
}
|
||||
fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
|
||||
let src = self.expander.in_file(ptr);
|
||||
let id = self.body.exprs.alloc(expr);
|
||||
self.source_map.expr_map_back.insert(id, src);
|
||||
// We intentionally don't fill this as it could overwrite a non-desugared entry
|
||||
// self.source_map.expr_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
fn missing_expr(&mut self) -> ExprId {
|
||||
self.alloc_expr_desugared(Expr::Missing)
|
||||
}
|
||||
|
|
|
@ -48,7 +48,16 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
|
|||
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.iter()).for_each(|(¶m, ty)| {
|
||||
let params = &db.function_data(it).params;
|
||||
let mut params = params.iter();
|
||||
if let Some(self_param) = body.self_param {
|
||||
p.print_binding(self_param);
|
||||
p.buf.push(':');
|
||||
if let Some(ty) = params.next() {
|
||||
p.print_type_ref(ty);
|
||||
}
|
||||
}
|
||||
body.params.iter().zip(params).for_each(|(¶m, ty)| {
|
||||
p.print_pat(param);
|
||||
p.buf.push(':');
|
||||
p.print_type_ref(ty);
|
||||
|
|
|
@ -96,6 +96,9 @@ impl ExprScopes {
|
|||
scope_by_expr: ArenaMap::with_capacity(body.exprs.len()),
|
||||
};
|
||||
let mut root = scopes.root_scope();
|
||||
if let Some(self_param) = body.self_param {
|
||||
scopes.add_bindings(body, root, self_param);
|
||||
}
|
||||
scopes.add_params_bindings(body, root, &body.params);
|
||||
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
|
||||
scopes
|
||||
|
|
|
@ -76,7 +76,7 @@ impl ChildBySource for ItemScope {
|
|||
self.extern_crate_decls()
|
||||
.for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::EXTERN_CRATE));
|
||||
self.use_decls().for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::USE));
|
||||
self.unnamed_consts(db)
|
||||
self.unnamed_consts()
|
||||
.for_each(|konst| insert_item_loc(db, res, file_id, konst, keys::CONST));
|
||||
self.attr_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
|
||||
|(ast_id, call_id)| {
|
||||
|
|
|
@ -715,7 +715,7 @@ impl<'a> AssocItemCollector<'a> {
|
|||
}
|
||||
AssocItem::MacroCall(call) => {
|
||||
let file_id = self.expander.current_file_id();
|
||||
let MacroCall { ast_id, expand_to, call_site, ref path } = item_tree[call];
|
||||
let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call];
|
||||
let module = self.expander.module.local_id;
|
||||
|
||||
let resolver = |path| {
|
||||
|
@ -734,7 +734,7 @@ impl<'a> AssocItemCollector<'a> {
|
|||
match macro_call_as_call_id(
|
||||
self.db.upcast(),
|
||||
&AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
|
||||
call_site,
|
||||
ctxt,
|
||||
expand_to,
|
||||
self.expander.module.krate(),
|
||||
resolver,
|
||||
|
@ -745,6 +745,7 @@ impl<'a> AssocItemCollector<'a> {
|
|||
self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike {
|
||||
ast_id: InFile::new(file_id, ast_id),
|
||||
expand_to: hir_expand::ExpandTo::Items,
|
||||
eager: None,
|
||||
});
|
||||
}
|
||||
Ok(None) => (),
|
||||
|
@ -754,6 +755,7 @@ impl<'a> AssocItemCollector<'a> {
|
|||
MacroCallKind::FnLike {
|
||||
ast_id: InFile::new(file_id, ast_id),
|
||||
expand_to,
|
||||
eager: None,
|
||||
},
|
||||
Clone::clone(path),
|
||||
));
|
||||
|
|
|
@ -191,9 +191,9 @@ impl StructData {
|
|||
let krate = loc.container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
|
||||
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
|
||||
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
|
||||
|
||||
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
|
||||
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
|
||||
|
||||
let mut flags = StructFlags::NO_FLAGS;
|
||||
if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
|
||||
|
@ -248,9 +248,9 @@ impl StructData {
|
|||
let krate = loc.container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
|
||||
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
|
||||
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
|
||||
|
||||
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
|
||||
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
|
||||
let mut flags = StructFlags::NO_FLAGS;
|
||||
if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
|
||||
flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
|
||||
|
|
|
@ -309,13 +309,9 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
|
|||
kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()),
|
||||
local_inner: false,
|
||||
allow_internal_unsafe: loc.allow_internal_unsafe,
|
||||
span: db
|
||||
.span_map(loc.id.file_id())
|
||||
.span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()),
|
||||
edition: loc.edition,
|
||||
}
|
||||
}
|
||||
|
||||
MacroId::MacroRulesId(it) => {
|
||||
let loc: MacroRulesLoc = it.lookup(db);
|
||||
|
||||
|
@ -328,9 +324,6 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
|
|||
allow_internal_unsafe: loc
|
||||
.flags
|
||||
.contains(MacroRulesLocFlags::ALLOW_INTERNAL_UNSAFE),
|
||||
span: db
|
||||
.span_map(loc.id.file_id())
|
||||
.span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()),
|
||||
edition: loc.edition,
|
||||
}
|
||||
}
|
||||
|
@ -348,9 +341,6 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
|
|||
),
|
||||
local_inner: false,
|
||||
allow_internal_unsafe: false,
|
||||
span: db
|
||||
.span_map(loc.id.file_id())
|
||||
.span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()),
|
||||
edition: loc.edition,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,30 +241,8 @@ impl ItemScope {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn unnamed_consts<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn DefDatabase,
|
||||
) -> impl Iterator<Item = ConstId> + 'a {
|
||||
// FIXME: Also treat consts named `_DERIVE_*` as unnamed, since synstructure generates those.
|
||||
// Should be removed once synstructure stops doing that.
|
||||
let synstructure_hack_consts = self.values.values().filter_map(|(item, _, _)| match item {
|
||||
&ModuleDefId::ConstId(id) => {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
if item_tree[loc.id.value]
|
||||
.name
|
||||
.as_ref()
|
||||
.map_or(false, |n| n.to_smol_str().starts_with("_DERIVE_"))
|
||||
{
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
self.unnamed_consts.iter().copied().chain(synstructure_hack_consts)
|
||||
pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ {
|
||||
self.unnamed_consts.iter().copied()
|
||||
}
|
||||
|
||||
/// Iterate over all module scoped macros
|
||||
|
|
|
@ -49,7 +49,7 @@ use intern::Interned;
|
|||
use la_arena::{Arena, Idx, IdxRange, RawIdx};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use span::{AstIdNode, FileAstId, Span};
|
||||
use span::{AstIdNode, FileAstId, SyntaxContextId};
|
||||
use stdx::never;
|
||||
use syntax::{ast, match_ast, SyntaxKind};
|
||||
use triomphe::Arc;
|
||||
|
@ -790,8 +790,7 @@ pub struct MacroCall {
|
|||
pub path: Interned<ModPath>,
|
||||
pub ast_id: FileAstId<ast::MacroCall>,
|
||||
pub expand_to: ExpandTo,
|
||||
// FIXME: We need to move this out. It invalidates the item tree when typing inside the macro call.
|
||||
pub call_site: Span,
|
||||
pub ctxt: SyntaxContextId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
|
|
@ -560,35 +560,32 @@ impl<'a> Ctx<'a> {
|
|||
|
||||
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
|
||||
let span_map = self.span_map();
|
||||
let path = Interned::new(ModPath::from_src(self.db.upcast(), m.path()?, &mut |range| {
|
||||
let path = m.path()?;
|
||||
let range = path.syntax().text_range();
|
||||
let path = Interned::new(ModPath::from_src(self.db.upcast(), path, &mut |range| {
|
||||
span_map.span_for_range(range).ctx
|
||||
})?);
|
||||
let ast_id = self.source_ast_id_map.ast_id(m);
|
||||
let expand_to = hir_expand::ExpandTo::from_call_site(m);
|
||||
let res = MacroCall {
|
||||
path,
|
||||
ast_id,
|
||||
expand_to,
|
||||
call_site: span_map.span_for_range(m.syntax().text_range()),
|
||||
};
|
||||
let res = MacroCall { path, ast_id, expand_to, ctxt: span_map.span_for_range(range).ctx };
|
||||
Some(id(self.data().macro_calls.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option<FileItemTreeId<MacroRules>> {
|
||||
let name = m.name().map(|it| it.as_name())?;
|
||||
let name = m.name()?;
|
||||
let ast_id = self.source_ast_id_map.ast_id(m);
|
||||
|
||||
let res = MacroRules { name, ast_id };
|
||||
let res = MacroRules { name: name.as_name(), ast_id };
|
||||
Some(id(self.data().macro_rules.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<FileItemTreeId<Macro2>> {
|
||||
let name = m.name().map(|it| it.as_name())?;
|
||||
let name = m.name()?;
|
||||
|
||||
let ast_id = self.source_ast_id_map.ast_id(m);
|
||||
let visibility = self.lower_visibility(m);
|
||||
|
||||
let res = Macro2 { name, ast_id, visibility };
|
||||
let res = Macro2 { name: name.as_name(), ast_id, visibility };
|
||||
Some(id(self.data().macro_defs.alloc(res)))
|
||||
}
|
||||
|
||||
|
|
|
@ -487,12 +487,12 @@ impl Printer<'_> {
|
|||
}
|
||||
}
|
||||
ModItem::MacroCall(it) => {
|
||||
let MacroCall { path, ast_id, expand_to, call_site } = &self.tree[it];
|
||||
let MacroCall { path, ast_id, expand_to, ctxt } = &self.tree[it];
|
||||
let _ = writeln!(
|
||||
self,
|
||||
"// AstId: {:?}, Span: {}, ExpandTo: {:?}",
|
||||
"// AstId: {:?}, SyntaxContext: {}, ExpandTo: {:?}",
|
||||
ast_id.erase().into_raw(),
|
||||
call_site,
|
||||
ctxt,
|
||||
expand_to
|
||||
);
|
||||
wln!(self, "{}!(...);", path.display(self.db.upcast()));
|
||||
|
|
|
@ -278,7 +278,7 @@ m!();
|
|||
// AstId: 2
|
||||
pub macro m2 { ... }
|
||||
|
||||
// AstId: 3, Span: 0:3@0..5#0, ExpandTo: Items
|
||||
// AstId: 3, SyntaxContext: 0, ExpandTo: Items
|
||||
m!(...);
|
||||
"#]],
|
||||
);
|
||||
|
|
|
@ -90,7 +90,7 @@ use hir_expand::{
|
|||
use item_tree::ExternBlock;
|
||||
use la_arena::Idx;
|
||||
use nameres::DefMap;
|
||||
use span::{AstIdNode, FileAstId, FileId, Span};
|
||||
use span::{AstIdNode, FileAstId, FileId, SyntaxContextId};
|
||||
use stdx::impl_from;
|
||||
use syntax::{ast, AstNode};
|
||||
|
||||
|
@ -1342,21 +1342,22 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
|||
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
|
||||
let span_map = db.span_map(self.file_id);
|
||||
let path = self.value.path().and_then(|path| {
|
||||
path::ModPath::from_src(db, path, &mut |range| {
|
||||
let range = path.syntax().text_range();
|
||||
let mod_path = path::ModPath::from_src(db, path, &mut |range| {
|
||||
span_map.as_ref().span_for_range(range).ctx
|
||||
})
|
||||
})?;
|
||||
let call_site = span_map.span_for_range(range);
|
||||
Some((call_site, mod_path))
|
||||
});
|
||||
|
||||
let Some(path) = path else {
|
||||
let Some((call_site, path)) = path else {
|
||||
return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
|
||||
};
|
||||
|
||||
let call_site = span_map.span_for_range(self.value.syntax().text_range());
|
||||
|
||||
macro_call_as_call_id_with_eager(
|
||||
db,
|
||||
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
|
||||
call_site,
|
||||
call_site.ctx,
|
||||
expands_to,
|
||||
krate,
|
||||
resolver,
|
||||
|
@ -1381,7 +1382,7 @@ impl<T: AstIdNode> AstIdWithPath<T> {
|
|||
fn macro_call_as_call_id(
|
||||
db: &dyn ExpandDatabase,
|
||||
call: &AstIdWithPath<ast::MacroCall>,
|
||||
call_site: Span,
|
||||
call_site: SyntaxContextId,
|
||||
expand_to: ExpandTo,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
|
@ -1393,7 +1394,7 @@ fn macro_call_as_call_id(
|
|||
fn macro_call_as_call_id_with_eager(
|
||||
db: &dyn ExpandDatabase,
|
||||
call: &AstIdWithPath<ast::MacroCall>,
|
||||
call_site: Span,
|
||||
call_site: SyntaxContextId,
|
||||
expand_to: ExpandTo,
|
||||
krate: CrateId,
|
||||
resolver: impl FnOnce(path::ModPath) -> Option<MacroDefId>,
|
||||
|
@ -1403,17 +1404,20 @@ fn macro_call_as_call_id_with_eager(
|
|||
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
|
||||
|
||||
let res = match def.kind {
|
||||
MacroDefKind::BuiltInEager(..) => {
|
||||
let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
|
||||
expand_eager_macro_input(db, krate, macro_call, def, call_site, &|path| {
|
||||
eager_resolver(path).filter(MacroDefId::is_fn_like)
|
||||
})
|
||||
}
|
||||
MacroDefKind::BuiltInEager(..) => expand_eager_macro_input(
|
||||
db,
|
||||
krate,
|
||||
&call.ast_id.to_node(db),
|
||||
call.ast_id,
|
||||
def,
|
||||
call_site,
|
||||
&|path| eager_resolver(path).filter(MacroDefId::is_fn_like),
|
||||
),
|
||||
_ if def.is_fn_like() => ExpandResult {
|
||||
value: Some(def.as_lazy_macro(
|
||||
value: Some(def.make_call(
|
||||
db,
|
||||
krate,
|
||||
MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
|
||||
MacroCallKind::FnLike { ast_id: call.ast_id, expand_to, eager: None },
|
||||
call_site,
|
||||
)),
|
||||
err: None,
|
||||
|
|
|
@ -528,3 +528,121 @@ impl < > $crate::fmt::Debug for Command< > where {
|
|||
}"#]],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_debug_expand_with_cfg() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: derive, fmt
|
||||
use core::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HideAndShow {
|
||||
#[cfg(never)]
|
||||
always_hide: u32,
|
||||
#[cfg(not(never))]
|
||||
always_show: u32,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
enum HideAndShowEnum {
|
||||
#[cfg(never)]
|
||||
AlwaysHide,
|
||||
#[cfg(not(never))]
|
||||
AlwaysShow{
|
||||
#[cfg(never)]
|
||||
always_hide: u32,
|
||||
#[cfg(not(never))]
|
||||
always_show: u32,
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
use core::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HideAndShow {
|
||||
#[cfg(never)]
|
||||
always_hide: u32,
|
||||
#[cfg(not(never))]
|
||||
always_show: u32,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
enum HideAndShowEnum {
|
||||
#[cfg(never)]
|
||||
AlwaysHide,
|
||||
#[cfg(not(never))]
|
||||
AlwaysShow{
|
||||
#[cfg(never)]
|
||||
always_hide: u32,
|
||||
#[cfg(not(never))]
|
||||
always_show: u32,
|
||||
}
|
||||
}
|
||||
|
||||
impl < > $crate::fmt::Debug for HideAndShow< > where {
|
||||
fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result {
|
||||
match self {
|
||||
HideAndShow {
|
||||
always_show: always_show,
|
||||
}
|
||||
=>f.debug_struct("HideAndShow").field("always_show", &always_show).finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
impl < > $crate::fmt::Debug for HideAndShowEnum< > where {
|
||||
fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result {
|
||||
match self {
|
||||
HideAndShowEnum::AlwaysShow {
|
||||
always_show: always_show,
|
||||
}
|
||||
=>f.debug_struct("AlwaysShow").field("always_show", &always_show).finish(),
|
||||
}
|
||||
}
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_default_expand_with_cfg() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: derive, default
|
||||
#[derive(Default)]
|
||||
struct Foo {
|
||||
field1: i32,
|
||||
#[cfg(never)]
|
||||
field2: (),
|
||||
}
|
||||
#[derive(Default)]
|
||||
enum Bar {
|
||||
Foo,
|
||||
#[cfg_attr(not(never), default)]
|
||||
Bar,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
#[derive(Default)]
|
||||
struct Foo {
|
||||
field1: i32,
|
||||
#[cfg(never)]
|
||||
field2: (),
|
||||
}
|
||||
#[derive(Default)]
|
||||
enum Bar {
|
||||
Foo,
|
||||
#[cfg_attr(not(never), default)]
|
||||
Bar,
|
||||
}
|
||||
|
||||
impl < > $crate::default::Default for Foo< > where {
|
||||
fn default() -> Self {
|
||||
Foo {
|
||||
field1: $crate::default::Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl < > $crate::default::Default for Bar< > where {
|
||||
fn default() -> Self {
|
||||
Bar::Bar
|
||||
}
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ fn main(foo: ()) {
|
|||
}
|
||||
|
||||
fn main(foo: ()) {
|
||||
/* error: unresolved macro unresolved */"helloworld!"#0:3@207..323#2#;
|
||||
/* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#0#;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ m!(&k");
|
|||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m { ($i:literal) => {}; }
|
||||
/* error: mismatched delimiters */"#]],
|
||||
/* error: expected literal */"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } }
|
|||
macro_rules! m2 { ($x:ident) => {} }
|
||||
|
||||
/* error: macro definition has parse errors */
|
||||
/* error: mismatched delimiters */
|
||||
/* error: expected ident */
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -61,15 +61,16 @@ use std::ops::Deref;
|
|||
|
||||
use base_db::{CrateId, Edition, FileId};
|
||||
use hir_expand::{
|
||||
name::Name, proc_macro::ProcMacroKind, HirFileId, InFile, MacroCallId, MacroDefId,
|
||||
name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use la_arena::Arena;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::FileAstId;
|
||||
use span::{FileAstId, ROOT_ERASED_FILE_AST_ID};
|
||||
use stdx::format_to;
|
||||
use syntax::{ast, SmolStr};
|
||||
use triomphe::Arc;
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
|
@ -677,6 +678,25 @@ impl ModuleData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn definition_source_range(&self, db: &dyn DefDatabase) -> InFile<TextRange> {
|
||||
match &self.origin {
|
||||
&ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => {
|
||||
InFile::new(
|
||||
definition.into(),
|
||||
ErasedAstId::new(definition.into(), ROOT_ERASED_FILE_AST_ID)
|
||||
.to_range(db.upcast()),
|
||||
)
|
||||
}
|
||||
&ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new(
|
||||
definition_tree_id.file_id(),
|
||||
AstId::new(definition_tree_id.file_id(), definition).to_range(db.upcast()),
|
||||
),
|
||||
ModuleOrigin::BlockExpr { block, .. } => {
|
||||
InFile::new(block.file_id, block.to_range(db.upcast()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root or block.
|
||||
pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option<InFile<ast::Module>> {
|
||||
|
@ -684,6 +704,13 @@ impl ModuleData {
|
|||
let value = decl.to_node(db.upcast());
|
||||
Some(InFile { file_id: decl.file_id, value })
|
||||
}
|
||||
|
||||
/// Returns the range which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root or block.
|
||||
pub fn declaration_source_range(&self, db: &dyn DefDatabase) -> Option<InFile<TextRange>> {
|
||||
let decl = self.origin.declaration()?;
|
||||
Some(InFile { file_id: decl.file_id, value: decl.to_range(db.upcast()) })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -5,7 +5,7 @@ use hir_expand::{
|
|||
attrs::{Attr, AttrId, AttrInput},
|
||||
MacroCallId, MacroCallKind, MacroDefId,
|
||||
};
|
||||
use span::Span;
|
||||
use span::SyntaxContextId;
|
||||
use syntax::{ast, SmolStr};
|
||||
use triomphe::Arc;
|
||||
|
||||
|
@ -109,14 +109,14 @@ pub(super) fn attr_macro_as_call_id(
|
|||
let arg = match macro_attr.input.as_deref() {
|
||||
Some(AttrInput::TokenTree(tt)) => {
|
||||
let mut tt = tt.as_ref().clone();
|
||||
tt.delimiter = tt::Delimiter::invisible_spanned(macro_attr.span);
|
||||
tt.delimiter.kind = tt::DelimiterKind::Invisible;
|
||||
Some(tt)
|
||||
}
|
||||
|
||||
_ => None,
|
||||
};
|
||||
|
||||
def.as_lazy_macro(
|
||||
def.make_call(
|
||||
db.upcast(),
|
||||
krate,
|
||||
MacroCallKind::Attr {
|
||||
|
@ -124,7 +124,7 @@ pub(super) fn attr_macro_as_call_id(
|
|||
attr_args: arg.map(Arc::new),
|
||||
invoc_attr_index: macro_attr.id,
|
||||
},
|
||||
macro_attr.span,
|
||||
macro_attr.ctxt,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -133,14 +133,14 @@ pub(super) fn derive_macro_as_call_id(
|
|||
item_attr: &AstIdWithPath<ast::Adt>,
|
||||
derive_attr_index: AttrId,
|
||||
derive_pos: u32,
|
||||
call_site: Span,
|
||||
call_site: SyntaxContextId,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
|
||||
) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
|
||||
let (macro_id, def_id) = resolver(item_attr.path.clone())
|
||||
.filter(|(_, def_id)| def_id.is_derive())
|
||||
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
|
||||
let call_id = def_id.as_lazy_macro(
|
||||
let call_id = def_id.make_call(
|
||||
db.upcast(),
|
||||
krate,
|
||||
MacroCallKind::Derive {
|
||||
|
|
|
@ -230,13 +230,13 @@ enum MacroDirectiveKind {
|
|||
FnLike {
|
||||
ast_id: AstIdWithPath<ast::MacroCall>,
|
||||
expand_to: ExpandTo,
|
||||
call_site: Span,
|
||||
ctxt: SyntaxContextId,
|
||||
},
|
||||
Derive {
|
||||
ast_id: AstIdWithPath<ast::Adt>,
|
||||
derive_attr: AttrId,
|
||||
derive_pos: usize,
|
||||
call_site: Span,
|
||||
ctxt: SyntaxContextId,
|
||||
},
|
||||
Attr {
|
||||
ast_id: AstIdWithPath<ast::Item>,
|
||||
|
@ -1126,7 +1126,7 @@ impl DefCollector<'_> {
|
|||
let resolver_def_id = |path| resolver(path).map(|(_, it)| it);
|
||||
|
||||
match &directive.kind {
|
||||
MacroDirectiveKind::FnLike { ast_id, expand_to, call_site } => {
|
||||
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
|
||||
let call_id = macro_call_as_call_id(
|
||||
self.db.upcast(),
|
||||
ast_id,
|
||||
|
@ -1146,7 +1146,7 @@ impl DefCollector<'_> {
|
|||
return Resolved::Yes;
|
||||
}
|
||||
}
|
||||
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site } => {
|
||||
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: call_site } => {
|
||||
let id = derive_macro_as_call_id(
|
||||
self.db,
|
||||
ast_id,
|
||||
|
@ -1266,7 +1266,7 @@ impl DefCollector<'_> {
|
|||
ast_id,
|
||||
derive_attr: attr.id,
|
||||
derive_pos: idx,
|
||||
call_site,
|
||||
ctxt: call_site.ctx,
|
||||
},
|
||||
container: directive.container,
|
||||
});
|
||||
|
@ -1428,7 +1428,7 @@ impl DefCollector<'_> {
|
|||
|
||||
for directive in &self.unresolved_macros {
|
||||
match &directive.kind {
|
||||
MacroDirectiveKind::FnLike { ast_id, expand_to, call_site } => {
|
||||
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
|
||||
// FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
|
||||
let macro_call_as_call_id = macro_call_as_call_id(
|
||||
self.db.upcast(),
|
||||
|
@ -1451,12 +1451,16 @@ impl DefCollector<'_> {
|
|||
if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
|
||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
directive.module_id,
|
||||
MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: *expand_to },
|
||||
MacroCallKind::FnLike {
|
||||
ast_id: ast_id.ast_id,
|
||||
expand_to: *expand_to,
|
||||
eager: None,
|
||||
},
|
||||
path,
|
||||
));
|
||||
}
|
||||
}
|
||||
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site: _ } => {
|
||||
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: _ } => {
|
||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
directive.module_id,
|
||||
MacroCallKind::Derive {
|
||||
|
@ -2285,7 +2289,7 @@ impl ModCollector<'_, '_> {
|
|||
|
||||
fn collect_macro_call(
|
||||
&mut self,
|
||||
&MacroCall { ref path, ast_id, expand_to, call_site }: &MacroCall,
|
||||
&MacroCall { ref path, ast_id, expand_to, ctxt }: &MacroCall,
|
||||
container: ItemContainerId,
|
||||
) {
|
||||
let ast_id = AstIdWithPath::new(self.file_id(), ast_id, ModPath::clone(path));
|
||||
|
@ -2299,7 +2303,7 @@ impl ModCollector<'_, '_> {
|
|||
if let Ok(res) = macro_call_as_call_id_with_eager(
|
||||
db.upcast(),
|
||||
&ast_id,
|
||||
call_site,
|
||||
ctxt,
|
||||
expand_to,
|
||||
self.def_collector.def_map.krate,
|
||||
|path| {
|
||||
|
@ -2357,7 +2361,7 @@ impl ModCollector<'_, '_> {
|
|||
self.def_collector.unresolved_macros.push(MacroDirective {
|
||||
module_id: self.module_id,
|
||||
depth: self.macro_depth + 1,
|
||||
kind: MacroDirectiveKind::FnLike { ast_id, expand_to, call_site },
|
||||
kind: MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt },
|
||||
container,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use base_db::{SourceDatabase, SourceDatabaseExt};
|
||||
use base_db::{SourceDatabase, SourceDatabaseExt2 as _};
|
||||
use test_fixture::WithFixture;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId};
|
||||
|
||||
|
@ -17,7 +16,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
|
|||
});
|
||||
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
|
||||
}
|
||||
db.set_file_text(pos.file_id, Arc::from(ra_fixture_change));
|
||||
db.set_file_text(pos.file_id, ra_fixture_change);
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
@ -267,7 +266,7 @@ fn quux() { 92 }
|
|||
m!(Y);
|
||||
m!(Z);
|
||||
"#;
|
||||
db.set_file_text(pos.file_id, Arc::from(new_text));
|
||||
db.set_file_text(pos.file_id, new_text);
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use either::Either;
|
||||
use hir_expand::InFile;
|
||||
use la_arena::ArenaMap;
|
||||
use syntax::ast;
|
||||
use syntax::{ast, AstNode, AstPtr};
|
||||
|
||||
use crate::{
|
||||
data::adt::lower_struct, db::DefDatabase, item_tree::ItemTreeNode, trace::Trace, GenericDefId,
|
||||
|
@ -12,8 +12,12 @@ use crate::{
|
|||
};
|
||||
|
||||
pub trait HasSource {
|
||||
type Value;
|
||||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
|
||||
type Value: AstNode;
|
||||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
|
||||
let InFile { file_id, value } = self.ast_ptr(db);
|
||||
InFile::new(file_id, value.to_node(&db.parse_or_expand(file_id)))
|
||||
}
|
||||
fn ast_ptr(&self, db: &dyn DefDatabase) -> InFile<AstPtr<Self::Value>>;
|
||||
}
|
||||
|
||||
impl<T> HasSource for T
|
||||
|
@ -22,16 +26,14 @@ where
|
|||
T::Id: ItemTreeNode,
|
||||
{
|
||||
type Value = <T::Id as ItemTreeNode>::Source;
|
||||
|
||||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
|
||||
fn ast_ptr(&self, db: &dyn DefDatabase) -> InFile<AstPtr<Self::Value>> {
|
||||
let id = self.item_tree_id();
|
||||
let file_id = id.file_id();
|
||||
let tree = id.item_tree(db);
|
||||
let ast_id_map = db.ast_id_map(file_id);
|
||||
let root = db.parse_or_expand(file_id);
|
||||
let node = &tree[id.value];
|
||||
|
||||
InFile::new(file_id, ast_id_map.get(node.ast_id()).to_node(&root))
|
||||
InFile::new(file_id, ast_id_map.get(node.ast_id()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use either::Either;
|
|||
use intern::Interned;
|
||||
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::Span;
|
||||
use span::{Span, SyntaxContextId};
|
||||
use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
|
||||
use triomphe::Arc;
|
||||
|
||||
|
@ -53,7 +53,7 @@ impl RawAttrs {
|
|||
id,
|
||||
input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
|
||||
path: Interned::new(ModPath::from(crate::name!(doc))),
|
||||
span: span_map.span_for_range(comment.syntax().text_range()),
|
||||
ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx,
|
||||
}),
|
||||
});
|
||||
let entries: Arc<[Attr]> = Arc::from_iter(entries);
|
||||
|
@ -173,7 +173,7 @@ pub struct Attr {
|
|||
pub id: AttrId,
|
||||
pub path: Interned<ModPath>,
|
||||
pub input: Option<Interned<AttrInput>>,
|
||||
pub span: Span,
|
||||
pub ctxt: SyntaxContextId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -201,10 +201,12 @@ impl Attr {
|
|||
span_map: SpanMapRef<'_>,
|
||||
id: AttrId,
|
||||
) -> Option<Attr> {
|
||||
let path = Interned::new(ModPath::from_src(db, ast.path()?, &mut |range| {
|
||||
let path = ast.path()?;
|
||||
let range = path.syntax().text_range();
|
||||
let path = Interned::new(ModPath::from_src(db, path, &mut |range| {
|
||||
span_map.span_for_range(range).ctx
|
||||
})?);
|
||||
let span = span_map.span_for_range(ast.syntax().text_range());
|
||||
let span = span_map.span_for_range(range);
|
||||
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
|
||||
let value = match lit.kind() {
|
||||
ast::LiteralKind::String(string) => string.value()?.into(),
|
||||
|
@ -217,11 +219,11 @@ impl Attr {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
Some(Attr { id, path, input, span })
|
||||
Some(Attr { id, path, input, ctxt: span.ctx })
|
||||
}
|
||||
|
||||
fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree], id: AttrId) -> Option<Attr> {
|
||||
let span = tt.first()?.first_span();
|
||||
let ctxt = tt.first()?.first_span().ctx;
|
||||
let path_end = tt
|
||||
.iter()
|
||||
.position(|tt| {
|
||||
|
@ -253,7 +255,7 @@ impl Attr {
|
|||
}
|
||||
_ => None,
|
||||
};
|
||||
Some(Attr { id, path, input, span })
|
||||
Some(Attr { id, path, input, ctxt })
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &ModPath {
|
||||
|
|
|
@ -11,7 +11,7 @@ macro_rules! register_builtin {
|
|||
}
|
||||
|
||||
impl BuiltinAttrExpander {
|
||||
pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree) -> ExpandResult<tt::Subtree> {
|
||||
pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult<tt::Subtree> {
|
||||
match *self {
|
||||
$( BuiltinAttrExpander::$variant => $expand, )*
|
||||
}
|
||||
|
@ -34,8 +34,9 @@ impl BuiltinAttrExpander {
|
|||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
self.expander()(db, id, tt)
|
||||
self.expander()(db, id, tt, span)
|
||||
}
|
||||
|
||||
pub fn is_derive(self) -> bool {
|
||||
|
@ -71,6 +72,7 @@ fn dummy_attr_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
_span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
ExpandResult::ok(tt.clone())
|
||||
}
|
||||
|
@ -100,6 +102,7 @@ fn derive_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let derives = match &loc.kind {
|
||||
|
@ -107,17 +110,14 @@ fn derive_expand(
|
|||
attr_args
|
||||
}
|
||||
_ => {
|
||||
return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan {
|
||||
open: loc.call_site,
|
||||
close: loc.call_site,
|
||||
}))
|
||||
return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan { open: span, close: span }))
|
||||
}
|
||||
};
|
||||
pseudo_derive_attr_expansion(tt, derives, loc.call_site)
|
||||
pseudo_derive_attr_expansion(tt, derives, span)
|
||||
}
|
||||
|
||||
pub fn pseudo_derive_attr_expansion(
|
||||
tt: &tt::Subtree,
|
||||
_: &tt::Subtree,
|
||||
args: &tt::Subtree,
|
||||
call_site: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
|
@ -141,7 +141,7 @@ pub fn pseudo_derive_attr_expansion(
|
|||
token_trees.push(mk_leaf(']'));
|
||||
}
|
||||
ExpandResult::ok(tt::Subtree {
|
||||
delimiter: tt.delimiter,
|
||||
delimiter: args.delimiter,
|
||||
token_trees: token_trees.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -50,8 +50,8 @@ impl BuiltinDeriveExpander {
|
|||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let span = db.lookup_intern_macro_call(id).call_site;
|
||||
let span = span_with_def_site_ctxt(db, span, id);
|
||||
self.expander()(span, tt)
|
||||
}
|
||||
|
|
|
@ -19,14 +19,14 @@ use crate::{
|
|||
};
|
||||
|
||||
macro_rules! register_builtin {
|
||||
( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
|
||||
( $LAZY:ident: $(($name:ident, $kind: ident) => $expand:ident),* , $EAGER:ident: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum BuiltinFnLikeExpander {
|
||||
pub enum $LAZY {
|
||||
$($kind),*
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum EagerExpander {
|
||||
pub enum $EAGER {
|
||||
$($e_kind),*
|
||||
}
|
||||
|
||||
|
@ -62,8 +62,8 @@ impl BuiltinFnLikeExpander {
|
|||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let span = db.lookup_intern_macro_call(id).call_site;
|
||||
let span = span_with_def_site_ctxt(db, span, id);
|
||||
self.expander()(db, id, tt, span)
|
||||
}
|
||||
|
@ -75,8 +75,8 @@ impl EagerExpander {
|
|||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let span = db.lookup_intern_macro_call(id).call_site;
|
||||
let span = span_with_def_site_ctxt(db, span, id);
|
||||
self.expander()(db, id, tt, span)
|
||||
}
|
||||
|
@ -84,6 +84,17 @@ impl EagerExpander {
|
|||
pub fn is_include(&self) -> bool {
|
||||
matches!(self, EagerExpander::Include)
|
||||
}
|
||||
|
||||
pub fn is_include_like(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
EagerExpander::Include | EagerExpander::IncludeStr | EagerExpander::IncludeBytes
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_env_or_option_env(&self) -> bool {
|
||||
matches!(self, EagerExpander::Env | EagerExpander::OptionEnv)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_builtin_macro(
|
||||
|
@ -93,7 +104,7 @@ pub fn find_builtin_macro(
|
|||
}
|
||||
|
||||
register_builtin! {
|
||||
LAZY:
|
||||
BuiltinFnLikeExpander:
|
||||
(column, Column) => line_expand,
|
||||
(file, File) => file_expand,
|
||||
(line, Line) => line_expand,
|
||||
|
@ -114,7 +125,7 @@ register_builtin! {
|
|||
(format_args_nl, FormatArgsNl) => format_args_nl_expand,
|
||||
(quote, Quote) => quote_expand,
|
||||
|
||||
EAGER:
|
||||
EagerExpander:
|
||||
(compile_error, CompileError) => compile_error_expand,
|
||||
(concat, Concat) => concat_expand,
|
||||
(concat_idents, ConcatIdents) => concat_idents_expand,
|
||||
|
@ -426,22 +437,25 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn unquote_str(lit: &tt::Literal) -> Option<String> {
|
||||
fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> {
|
||||
let span = lit.span;
|
||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||
let token = ast::String::cast(lit)?;
|
||||
token.value().map(|it| it.into_owned())
|
||||
token.value().map(|it| (it.into_owned(), span))
|
||||
}
|
||||
|
||||
fn unquote_char(lit: &tt::Literal) -> Option<char> {
|
||||
fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> {
|
||||
let span = lit.span;
|
||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||
let token = ast::Char::cast(lit)?;
|
||||
token.value()
|
||||
token.value().zip(Some(span))
|
||||
}
|
||||
|
||||
fn unquote_byte_string(lit: &tt::Literal) -> Option<Vec<u8>> {
|
||||
fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec<u8>, Span)> {
|
||||
let span = lit.span;
|
||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||
let token = ast::ByteString::cast(lit)?;
|
||||
token.value().map(|it| it.into_owned())
|
||||
token.value().map(|it| (it.into_owned(), span))
|
||||
}
|
||||
|
||||
fn compile_error_expand(
|
||||
|
@ -452,7 +466,7 @@ fn compile_error_expand(
|
|||
) -> ExpandResult<tt::Subtree> {
|
||||
let err = match &*tt.token_trees {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
|
||||
Some(unquoted) => ExpandError::other(unquoted.into_boxed_str()),
|
||||
Some((unquoted, _)) => ExpandError::other(unquoted.into_boxed_str()),
|
||||
None => ExpandError::other("`compile_error!` argument must be a string"),
|
||||
},
|
||||
_ => ExpandError::other("`compile_error!` argument must be a string"),
|
||||
|
@ -465,10 +479,16 @@ fn concat_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
_: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let mut err = None;
|
||||
let mut text = String::new();
|
||||
let mut span: Option<Span> = None;
|
||||
let mut record_span = |s: Span| match &mut span {
|
||||
Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range),
|
||||
Some(_) => (),
|
||||
None => span = Some(s),
|
||||
};
|
||||
for (i, mut t) in tt.token_trees.iter().enumerate() {
|
||||
// FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses
|
||||
// to ensure the right parsing order, so skip the parentheses here. Ideally we'd
|
||||
|
@ -486,11 +506,14 @@ fn concat_expand(
|
|||
// concat works with string and char literals, so remove any quotes.
|
||||
// It also works with integer, float and boolean literals, so just use the rest
|
||||
// as-is.
|
||||
if let Some(c) = unquote_char(it) {
|
||||
if let Some((c, span)) = unquote_char(it) {
|
||||
text.push(c);
|
||||
record_span(span);
|
||||
} else {
|
||||
let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
|
||||
let (component, span) =
|
||||
unquote_str(it).unwrap_or_else(|| (it.text.to_string(), it.span));
|
||||
text.push_str(&component);
|
||||
record_span(span);
|
||||
}
|
||||
}
|
||||
// handle boolean literals
|
||||
|
@ -498,6 +521,7 @@ fn concat_expand(
|
|||
if i % 2 == 0 && (id.text == "true" || id.text == "false") =>
|
||||
{
|
||||
text.push_str(id.text.as_str());
|
||||
record_span(id.span);
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
||||
_ => {
|
||||
|
@ -505,6 +529,7 @@ fn concat_expand(
|
|||
}
|
||||
}
|
||||
}
|
||||
let span = span.unwrap_or(tt.delimiter.open);
|
||||
ExpandResult { value: quote!(span =>#text), err }
|
||||
}
|
||||
|
||||
|
@ -512,18 +537,25 @@ fn concat_bytes_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
call_site: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let mut bytes = Vec::new();
|
||||
let mut err = None;
|
||||
let mut span: Option<Span> = None;
|
||||
let mut record_span = |s: Span| match &mut span {
|
||||
Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range),
|
||||
Some(_) => (),
|
||||
None => span = Some(s),
|
||||
};
|
||||
for (i, t) in tt.token_trees.iter().enumerate() {
|
||||
match t {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
|
||||
let token = ast::make::tokens::literal(&lit.to_string());
|
||||
record_span(lit.span);
|
||||
match token.kind() {
|
||||
syntax::SyntaxKind::BYTE => bytes.push(token.text().to_owned()),
|
||||
syntax::SyntaxKind::BYTE_STRING => {
|
||||
let components = unquote_byte_string(lit).unwrap_or_default();
|
||||
let components = unquote_byte_string(lit).map_or(vec![], |(it, _)| it);
|
||||
components.into_iter().for_each(|it| bytes.push(it.to_string()));
|
||||
}
|
||||
_ => {
|
||||
|
@ -534,7 +566,7 @@ fn concat_bytes_expand(
|
|||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
||||
tt::TokenTree::Subtree(tree) if tree.delimiter.kind == tt::DelimiterKind::Bracket => {
|
||||
if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes) {
|
||||
if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes, &mut record_span) {
|
||||
err.get_or_insert(e);
|
||||
break;
|
||||
}
|
||||
|
@ -546,17 +578,24 @@ fn concat_bytes_expand(
|
|||
}
|
||||
}
|
||||
let value = tt::Subtree {
|
||||
delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket },
|
||||
delimiter: tt::Delimiter {
|
||||
open: call_site,
|
||||
close: call_site,
|
||||
kind: tt::DelimiterKind::Bracket,
|
||||
},
|
||||
token_trees: {
|
||||
Itertools::intersperse_with(
|
||||
bytes.into_iter().map(|it| {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text: it.into(), span }))
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
text: it.into(),
|
||||
span: span.unwrap_or(call_site),
|
||||
}))
|
||||
}),
|
||||
|| {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||
char: ',',
|
||||
spacing: tt::Spacing::Alone,
|
||||
span,
|
||||
span: call_site,
|
||||
}))
|
||||
},
|
||||
)
|
||||
|
@ -569,13 +608,15 @@ fn concat_bytes_expand(
|
|||
fn concat_bytes_expand_subtree(
|
||||
tree: &tt::Subtree,
|
||||
bytes: &mut Vec<String>,
|
||||
mut record_span: impl FnMut(Span),
|
||||
) -> Result<(), ExpandError> {
|
||||
for (ti, tt) in tree.token_trees.iter().enumerate() {
|
||||
match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
|
||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => {
|
||||
let lit = ast::make::tokens::literal(&it.to_string());
|
||||
match lit.kind() {
|
||||
syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => {
|
||||
record_span(it.span);
|
||||
bytes.push(lit.text().to_owned())
|
||||
}
|
||||
_ => {
|
||||
|
@ -635,7 +676,7 @@ fn relative_file(
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_string(tt: &tt::Subtree) -> Result<String, ExpandError> {
|
||||
fn parse_string(tt: &tt::Subtree) -> Result<(String, Span), ExpandError> {
|
||||
tt.token_trees
|
||||
.first()
|
||||
.and_then(|tt| match tt {
|
||||
|
@ -675,7 +716,7 @@ pub fn include_input_to_file_id(
|
|||
arg_id: MacroCallId,
|
||||
arg: &tt::Subtree,
|
||||
) -> Result<FileId, ExpandError> {
|
||||
relative_file(db, arg_id, &parse_string(arg)?, false)
|
||||
relative_file(db, arg_id, &parse_string(arg)?.0, false)
|
||||
}
|
||||
|
||||
fn include_bytes_expand(
|
||||
|
@ -701,7 +742,7 @@ fn include_str_expand(
|
|||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let path = match parse_string(tt) {
|
||||
let (path, span) = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => {
|
||||
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
|
||||
|
@ -736,7 +777,7 @@ fn env_expand(
|
|||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let key = match parse_string(tt) {
|
||||
let (key, span) = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => {
|
||||
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
|
||||
|
@ -766,18 +807,24 @@ fn option_env_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
call_site: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let key = match parse_string(tt) {
|
||||
let (key, span) = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => {
|
||||
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
|
||||
return ExpandResult::new(
|
||||
tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }),
|
||||
e,
|
||||
)
|
||||
}
|
||||
};
|
||||
let dollar_crate = dollar_crate(span);
|
||||
let dollar_crate = dollar_crate(call_site);
|
||||
let expanded = match get_env_inner(db, arg_id, &key) {
|
||||
None => quote! {span => #dollar_crate::option::Option::None::<&str> },
|
||||
Some(s) => quote! {span => #dollar_crate::option::Option::Some(#s) },
|
||||
None => quote! {call_site => #dollar_crate::option::Option::None::<&str> },
|
||||
Some(s) => {
|
||||
let s = quote! (span => #s);
|
||||
quote! {call_site => #dollar_crate::option::Option::Some(#s) }
|
||||
}
|
||||
};
|
||||
|
||||
ExpandResult::ok(expanded)
|
||||
|
|
327
crates/hir-expand/src/cfg_process.rs
Normal file
327
crates/hir-expand/src/cfg_process.rs
Normal file
|
@ -0,0 +1,327 @@
|
|||
//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro
|
||||
use std::iter::Peekable;
|
||||
|
||||
use cfg::{CfgAtom, CfgExpr};
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{
|
||||
ast::{self, Attr, HasAttrs, Meta, VariantList},
|
||||
AstNode, NodeOrToken, SyntaxElement, SyntaxNode, T,
|
||||
};
|
||||
use tracing::{debug, warn};
|
||||
use tt::SmolStr;
|
||||
|
||||
use crate::{db::ExpandDatabase, MacroCallKind, MacroCallLoc};
|
||||
|
||||
fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option<bool> {
|
||||
if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
|
||||
return None;
|
||||
}
|
||||
debug!("Evaluating cfg {}", attr);
|
||||
let cfg = parse_from_attr_meta(attr.meta()?)?;
|
||||
debug!("Checking cfg {:?}", cfg);
|
||||
let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg) != Some(false);
|
||||
Some(enabled)
|
||||
}
|
||||
|
||||
fn check_cfg_attr_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option<bool> {
|
||||
if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? {
|
||||
return None;
|
||||
}
|
||||
debug!("Evaluating cfg_attr {}", attr);
|
||||
let cfg_expr = parse_from_attr_meta(attr.meta()?)?;
|
||||
debug!("Checking cfg_attr {:?}", cfg_expr);
|
||||
let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg_expr) != Some(false);
|
||||
Some(enabled)
|
||||
}
|
||||
|
||||
fn process_has_attrs_with_possible_comma<I: HasAttrs>(
|
||||
items: impl Iterator<Item = I>,
|
||||
loc: &MacroCallLoc,
|
||||
db: &dyn ExpandDatabase,
|
||||
remove: &mut FxHashSet<SyntaxElement>,
|
||||
) -> Option<()> {
|
||||
for item in items {
|
||||
let field_attrs = item.attrs();
|
||||
'attrs: for attr in field_attrs {
|
||||
if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() {
|
||||
debug!("censoring type {:?}", item.syntax());
|
||||
remove.insert(item.syntax().clone().into());
|
||||
// We need to remove the , as well
|
||||
remove_possible_comma(&item, remove);
|
||||
break 'attrs;
|
||||
}
|
||||
|
||||
if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) {
|
||||
if enabled {
|
||||
debug!("Removing cfg_attr tokens {:?}", attr);
|
||||
let meta = attr.meta()?;
|
||||
let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?;
|
||||
remove.extend(removes_from_cfg_attr);
|
||||
} else {
|
||||
debug!("censoring type cfg_attr {:?}", item.syntax());
|
||||
remove.insert(attr.syntax().clone().into());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
enum CfgExprStage {
|
||||
/// Stripping the CFGExpr part of the attribute
|
||||
StrippigCfgExpr,
|
||||
/// Found the comma after the CFGExpr. Will keep all tokens until the next comma or the end of the attribute
|
||||
FoundComma,
|
||||
/// Everything following the attribute. This could be another attribute or the end of the attribute.
|
||||
// FIXME: cfg_attr with multiple attributes will not be handled correctly. We will only keep the first attribute
|
||||
// Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110
|
||||
EverythingElse,
|
||||
}
|
||||
/// This function creates its own set of tokens to remove. To help prevent malformed syntax as input.
|
||||
fn remove_tokens_within_cfg_attr(meta: Meta) -> Option<FxHashSet<SyntaxElement>> {
|
||||
let mut remove: FxHashSet<SyntaxElement> = FxHashSet::default();
|
||||
debug!("Enabling attribute {}", meta);
|
||||
let meta_path = meta.path()?;
|
||||
debug!("Removing {:?}", meta_path.syntax());
|
||||
remove.insert(meta_path.syntax().clone().into());
|
||||
|
||||
let meta_tt = meta.token_tree()?;
|
||||
debug!("meta_tt {}", meta_tt);
|
||||
let mut stage = CfgExprStage::StrippigCfgExpr;
|
||||
for tt in meta_tt.token_trees_and_tokens() {
|
||||
debug!("Checking {:?}. Stage: {:?}", tt, stage);
|
||||
match (stage, tt) {
|
||||
(CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Node(node)) => {
|
||||
remove.insert(node.syntax().clone().into());
|
||||
}
|
||||
(CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Token(token)) => {
|
||||
if token.kind() == T![,] {
|
||||
stage = CfgExprStage::FoundComma;
|
||||
}
|
||||
remove.insert(token.into());
|
||||
}
|
||||
(CfgExprStage::FoundComma, syntax::NodeOrToken::Token(token))
|
||||
if (token.kind() == T![,] || token.kind() == T![')']) =>
|
||||
{
|
||||
// The end of the attribute or separator for the next attribute
|
||||
stage = CfgExprStage::EverythingElse;
|
||||
remove.insert(token.into());
|
||||
}
|
||||
(CfgExprStage::EverythingElse, syntax::NodeOrToken::Node(node)) => {
|
||||
remove.insert(node.syntax().clone().into());
|
||||
}
|
||||
(CfgExprStage::EverythingElse, syntax::NodeOrToken::Token(token)) => {
|
||||
remove.insert(token.into());
|
||||
}
|
||||
// This is an actual attribute
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if stage != CfgExprStage::EverythingElse {
|
||||
warn!("Invalid cfg_attr attribute. {:?}", meta_tt);
|
||||
return None;
|
||||
}
|
||||
Some(remove)
|
||||
}
|
||||
/// Removes a possible comma after the [AstNode]
|
||||
fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet<SyntaxElement>) {
|
||||
if let Some(comma) = item.syntax().next_sibling_or_token().filter(|it| it.kind() == T![,]) {
|
||||
res.insert(comma);
|
||||
}
|
||||
}
|
||||
fn process_enum(
|
||||
variants: VariantList,
|
||||
loc: &MacroCallLoc,
|
||||
db: &dyn ExpandDatabase,
|
||||
remove: &mut FxHashSet<SyntaxElement>,
|
||||
) -> Option<()> {
|
||||
'variant: for variant in variants.variants() {
|
||||
for attr in variant.attrs() {
|
||||
if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() {
|
||||
// Rustc does not strip the attribute if it is enabled. So we will will leave it
|
||||
debug!("censoring type {:?}", variant.syntax());
|
||||
remove.insert(variant.syntax().clone().into());
|
||||
// We need to remove the , as well
|
||||
remove_possible_comma(&variant, remove);
|
||||
continue 'variant;
|
||||
};
|
||||
|
||||
if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) {
|
||||
if enabled {
|
||||
debug!("Removing cfg_attr tokens {:?}", attr);
|
||||
let meta = attr.meta()?;
|
||||
let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?;
|
||||
remove.extend(removes_from_cfg_attr);
|
||||
} else {
|
||||
debug!("censoring type cfg_attr {:?}", variant.syntax());
|
||||
remove.insert(attr.syntax().clone().into());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(fields) = variant.field_list() {
|
||||
match fields {
|
||||
ast::FieldList::RecordFieldList(fields) => {
|
||||
process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?;
|
||||
}
|
||||
ast::FieldList::TupleFieldList(fields) => {
|
||||
process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub(crate) fn process_cfg_attrs(
|
||||
node: &SyntaxNode,
|
||||
loc: &MacroCallLoc,
|
||||
db: &dyn ExpandDatabase,
|
||||
) -> Option<FxHashSet<SyntaxElement>> {
|
||||
// FIXME: #[cfg_eval] is not implemented. But it is not stable yet
|
||||
if !matches!(loc.kind, MacroCallKind::Derive { .. }) {
|
||||
return None;
|
||||
}
|
||||
let mut remove = FxHashSet::default();
|
||||
|
||||
let item = ast::Item::cast(node.clone())?;
|
||||
for attr in item.attrs() {
|
||||
if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) {
|
||||
if enabled {
|
||||
debug!("Removing cfg_attr tokens {:?}", attr);
|
||||
let meta = attr.meta()?;
|
||||
let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?;
|
||||
remove.extend(removes_from_cfg_attr);
|
||||
} else {
|
||||
debug!("censoring type cfg_attr {:?}", item.syntax());
|
||||
remove.insert(attr.syntax().clone().into());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
match item {
|
||||
ast::Item::Struct(it) => match it.field_list()? {
|
||||
ast::FieldList::RecordFieldList(fields) => {
|
||||
process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?;
|
||||
}
|
||||
ast::FieldList::TupleFieldList(fields) => {
|
||||
process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?;
|
||||
}
|
||||
},
|
||||
ast::Item::Enum(it) => {
|
||||
process_enum(it.variant_list()?, loc, db, &mut remove)?;
|
||||
}
|
||||
ast::Item::Union(it) => {
|
||||
process_has_attrs_with_possible_comma(
|
||||
it.record_field_list()?.fields(),
|
||||
loc,
|
||||
db,
|
||||
&mut remove,
|
||||
)?;
|
||||
}
|
||||
// FIXME: Implement for other items if necessary. As we do not support #[cfg_eval] yet, we do not need to implement it for now
|
||||
_ => {}
|
||||
}
|
||||
Some(remove)
|
||||
}
|
||||
/// Parses a `cfg` attribute from the meta
|
||||
fn parse_from_attr_meta(meta: Meta) -> Option<CfgExpr> {
|
||||
let tt = meta.token_tree()?;
|
||||
let mut iter = tt.token_trees_and_tokens().skip(1).peekable();
|
||||
next_cfg_expr_from_syntax(&mut iter)
|
||||
}
|
||||
|
||||
fn next_cfg_expr_from_syntax<I>(iter: &mut Peekable<I>) -> Option<CfgExpr>
|
||||
where
|
||||
I: Iterator<Item = NodeOrToken<ast::TokenTree, syntax::SyntaxToken>>,
|
||||
{
|
||||
let name = match iter.next() {
|
||||
None => return None,
|
||||
Some(NodeOrToken::Token(element)) => match element.kind() {
|
||||
syntax::T![ident] => SmolStr::new(element.text()),
|
||||
_ => return Some(CfgExpr::Invalid),
|
||||
},
|
||||
Some(_) => return Some(CfgExpr::Invalid),
|
||||
};
|
||||
let result = match name.as_str() {
|
||||
"all" | "any" | "not" => {
|
||||
let mut preds = Vec::new();
|
||||
let Some(NodeOrToken::Node(tree)) = iter.next() else {
|
||||
return Some(CfgExpr::Invalid);
|
||||
};
|
||||
let mut tree_iter = tree.token_trees_and_tokens().skip(1).peekable();
|
||||
while tree_iter
|
||||
.peek()
|
||||
.filter(
|
||||
|element| matches!(element, NodeOrToken::Token(token) if (token.kind() != syntax::T![')'])),
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
let pred = next_cfg_expr_from_syntax(&mut tree_iter);
|
||||
if let Some(pred) = pred {
|
||||
preds.push(pred);
|
||||
}
|
||||
}
|
||||
let group = match name.as_str() {
|
||||
"all" => CfgExpr::All(preds),
|
||||
"any" => CfgExpr::Any(preds),
|
||||
"not" => CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Some(group)
|
||||
}
|
||||
_ => match iter.peek() {
|
||||
Some(NodeOrToken::Token(element)) if (element.kind() == syntax::T![=]) => {
|
||||
iter.next();
|
||||
match iter.next() {
|
||||
Some(NodeOrToken::Token(value_token))
|
||||
if (value_token.kind() == syntax::SyntaxKind::STRING) =>
|
||||
{
|
||||
let value = value_token.text();
|
||||
let value = SmolStr::new(value.trim_matches('"'));
|
||||
Some(CfgExpr::Atom(CfgAtom::KeyValue { key: name, value }))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => Some(CfgExpr::Atom(CfgAtom::Flag(name))),
|
||||
},
|
||||
};
|
||||
if let Some(NodeOrToken::Token(element)) = iter.peek() {
|
||||
if element.kind() == syntax::T![,] {
|
||||
iter.next();
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cfg::DnfExpr;
|
||||
use expect_test::{expect, Expect};
|
||||
use syntax::{ast::Attr, AstNode, SourceFile};
|
||||
|
||||
use crate::cfg_process::parse_from_attr_meta;
|
||||
|
||||
fn check_dnf_from_syntax(input: &str, expect: Expect) {
|
||||
let parse = SourceFile::parse(input);
|
||||
let node = match parse.tree().syntax().descendants().find_map(Attr::cast) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
let node = std::any::type_name::<Attr>();
|
||||
panic!("Failed to make ast node `{node}` from text {input}")
|
||||
}
|
||||
};
|
||||
let node = node.clone_subtree();
|
||||
assert_eq!(node.syntax().text_range().start(), 0.into());
|
||||
|
||||
let cfg = parse_from_attr_meta(node.meta().unwrap()).unwrap();
|
||||
let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
#[test]
|
||||
fn cfg_from_attr() {
|
||||
check_dnf_from_syntax(r#"#[cfg(test)]"#, expect![[r#"#![cfg(test)]"#]]);
|
||||
check_dnf_from_syntax(r#"#[cfg(not(never))]"#, expect![[r#"#![cfg(not(never))]"#]]);
|
||||
}
|
||||
}
|
|
@ -48,7 +48,7 @@ impl ChangeWithProcMacros {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<str>>) {
|
||||
pub fn change_file(&mut self, file_id: FileId, new_text: Option<String>) {
|
||||
self.source_change.change_file(file_id, new_text)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,21 +3,19 @@
|
|||
use base_db::{salsa, CrateId, FileId, SourceDatabase};
|
||||
use either::Either;
|
||||
use limit::Limit;
|
||||
use mbe::{syntax_node_to_token_tree, ValueResult};
|
||||
use mbe::syntax_node_to_token_tree;
|
||||
use rustc_hash::FxHashSet;
|
||||
use span::{AstIdMap, SyntaxContextData, SyntaxContextId};
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs},
|
||||
AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
|
||||
};
|
||||
use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId};
|
||||
use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
attrs::collect_attrs,
|
||||
attrs::{collect_attrs, AttrId},
|
||||
builtin_attr_macro::pseudo_derive_attr_expansion,
|
||||
builtin_fn_macro::EagerExpander,
|
||||
cfg_process,
|
||||
declarative::DeclarativeMacroExpander,
|
||||
fixup::{self, reverse_fixups, SyntaxFixupUndoInfo},
|
||||
fixup::{self, SyntaxFixupUndoInfo},
|
||||
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
|
||||
proc_macro::ProcMacros,
|
||||
span_map::{RealSpanMap, SpanMap, SpanMapRef},
|
||||
|
@ -100,10 +98,7 @@ pub trait ExpandDatabase: SourceDatabase {
|
|||
/// Lowers syntactic macro call to a token tree representation. That's a firewall
|
||||
/// query, only typing in the macro call itself changes the returned
|
||||
/// subtree.
|
||||
fn macro_arg(
|
||||
&self,
|
||||
id: MacroCallId,
|
||||
) -> ValueResult<(Arc<tt::Subtree>, SyntaxFixupUndoInfo), Arc<Box<[SyntaxError]>>>;
|
||||
fn macro_arg(&self, id: MacroCallId) -> (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span);
|
||||
/// Fetches the expander for this macro.
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(TokenExpander::macro_expander)]
|
||||
|
@ -120,6 +115,12 @@ pub trait ExpandDatabase: SourceDatabase {
|
|||
/// non-determinism breaks salsa in a very, very, very bad way.
|
||||
/// @edwin0cheng heroically debugged this once! See #4315 for details
|
||||
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>;
|
||||
/// Retrieves the span to be used for a proc-macro expansions spans.
|
||||
/// This is a firewall query as it requires parsing the file, which we don't want proc-macros to
|
||||
/// directly depend on as that would cause to frequent invalidations, mainly because of the
|
||||
/// parse queries being LRU cached. If they weren't the invalidations would only happen if the
|
||||
/// user wrote in the file that defines the proc-macro.
|
||||
fn proc_macro_span(&self, fun: AstId<ast::Fn>) -> Span;
|
||||
/// Firewall query that returns the errors from the `parse_macro_expansion` query.
|
||||
fn parse_macro_expansion_error(
|
||||
&self,
|
||||
|
@ -139,30 +140,50 @@ pub fn expand_speculative(
|
|||
) -> Option<(SyntaxNode, SyntaxToken)> {
|
||||
let loc = db.lookup_intern_macro_call(actual_macro_call);
|
||||
|
||||
// FIXME: This BOGUS here is dangerous once the proc-macro server can call back into the database!
|
||||
let span_map = RealSpanMap::absolute(FileId::BOGUS);
|
||||
let span_map = SpanMapRef::RealSpanMap(&span_map);
|
||||
|
||||
let (_, _, span) = db.macro_arg(actual_macro_call);
|
||||
|
||||
// Build the subtree and token mapping for the speculative args
|
||||
let (mut tt, undo_info) = match loc.kind {
|
||||
MacroCallKind::FnLike { .. } => (
|
||||
mbe::syntax_node_to_token_tree(speculative_args, span_map, loc.call_site),
|
||||
mbe::syntax_node_to_token_tree(speculative_args, span_map, span),
|
||||
SyntaxFixupUndoInfo::NONE,
|
||||
),
|
||||
MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => {
|
||||
let censor = censor_for_macro_input(&loc, speculative_args);
|
||||
let mut fixups = fixup::fixup_syntax(span_map, speculative_args, loc.call_site);
|
||||
MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => (
|
||||
mbe::syntax_node_to_token_tree(speculative_args, span_map, span),
|
||||
SyntaxFixupUndoInfo::NONE,
|
||||
),
|
||||
MacroCallKind::Derive { derive_attr_index: index, .. }
|
||||
| MacroCallKind::Attr { invoc_attr_index: index, .. } => {
|
||||
let censor = if let MacroCallKind::Derive { .. } = loc.kind {
|
||||
censor_derive_input(index, &ast::Adt::cast(speculative_args.clone())?)
|
||||
} else {
|
||||
attr_source(index, &ast::Item::cast(speculative_args.clone())?)
|
||||
.into_iter()
|
||||
.map(|it| it.syntax().clone().into())
|
||||
.collect()
|
||||
};
|
||||
|
||||
let censor_cfg =
|
||||
cfg_process::process_cfg_attrs(speculative_args, &loc, db).unwrap_or_default();
|
||||
let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span);
|
||||
fixups.append.retain(|it, _| match it {
|
||||
syntax::NodeOrToken::Node(it) => !censor.contains(it),
|
||||
syntax::NodeOrToken::Token(_) => true,
|
||||
it => !censor.contains(it) && !censor_cfg.contains(it),
|
||||
});
|
||||
fixups.remove.extend(censor);
|
||||
fixups.remove.extend(censor_cfg);
|
||||
|
||||
(
|
||||
mbe::syntax_node_to_token_tree_modified(
|
||||
speculative_args,
|
||||
span_map,
|
||||
fixups.append,
|
||||
fixups.remove,
|
||||
loc.call_site,
|
||||
span,
|
||||
),
|
||||
fixups.undo_info,
|
||||
)
|
||||
|
@ -184,9 +205,8 @@ pub fn expand_speculative(
|
|||
}?;
|
||||
match attr.token_tree() {
|
||||
Some(token_tree) => {
|
||||
let mut tree =
|
||||
syntax_node_to_token_tree(token_tree.syntax(), span_map, loc.call_site);
|
||||
tree.delimiter = tt::Delimiter::invisible_spanned(loc.call_site);
|
||||
let mut tree = syntax_node_to_token_tree(token_tree.syntax(), span_map, span);
|
||||
tree.delimiter = tt::Delimiter::invisible_spanned(span);
|
||||
|
||||
Some(tree)
|
||||
}
|
||||
|
@ -199,36 +219,36 @@ pub fn expand_speculative(
|
|||
// Do the actual expansion, we need to directly expand the proc macro due to the attribute args
|
||||
// Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
|
||||
let mut speculative_expansion = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(expander, ..) => {
|
||||
tt.delimiter = tt::Delimiter::invisible_spanned(loc.call_site);
|
||||
MacroDefKind::ProcMacro(expander, _, ast) => {
|
||||
let span = db.proc_macro_span(ast);
|
||||
tt.delimiter = tt::Delimiter::invisible_spanned(span);
|
||||
expander.expand(
|
||||
db,
|
||||
loc.def.krate,
|
||||
loc.krate,
|
||||
&tt,
|
||||
attr_arg.as_ref(),
|
||||
span_with_def_site_ctxt(db, loc.def.span, actual_macro_call),
|
||||
span_with_call_site_ctxt(db, loc.def.span, actual_macro_call),
|
||||
span_with_mixed_site_ctxt(db, loc.def.span, actual_macro_call),
|
||||
span_with_def_site_ctxt(db, span, actual_macro_call),
|
||||
span_with_call_site_ctxt(db, span, actual_macro_call),
|
||||
span_with_mixed_site_ctxt(db, span, actual_macro_call),
|
||||
)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
|
||||
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, loc.call_site)
|
||||
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
|
||||
}
|
||||
MacroDefKind::Declarative(it) => {
|
||||
db.decl_macro_expander(loc.krate, it).expand_unhygienic(db, tt, loc.def.krate, span)
|
||||
}
|
||||
MacroDefKind::BuiltIn(it, _) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::Declarative(it) => db.decl_macro_expander(loc.krate, it).expand_unhygienic(
|
||||
db,
|
||||
tt,
|
||||
loc.def.krate,
|
||||
loc.call_site,
|
||||
),
|
||||
MacroDefKind::BuiltIn(it, _) => it.expand(db, actual_macro_call, &tt).map_err(Into::into),
|
||||
MacroDefKind::BuiltInDerive(it, ..) => {
|
||||
it.expand(db, actual_macro_call, &tt).map_err(Into::into)
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInEager(it, _) => {
|
||||
it.expand(db, actual_macro_call, &tt).map_err(Into::into)
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt),
|
||||
MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span),
|
||||
};
|
||||
|
||||
let expand_to = loc.expand_to();
|
||||
|
@ -319,181 +339,161 @@ pub(crate) fn parse_with_map(
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: for derive attributes, this will return separate copies of the same structures!
|
||||
// FIXME: for derive attributes, this will return separate copies of the same structures! Though
|
||||
// they may differ in spans due to differing call sites...
|
||||
fn macro_arg(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
// FIXME: consider the following by putting fixup info into eager call info args
|
||||
// ) -> ValueResult<Arc<(tt::Subtree, SyntaxFixupUndoInfo)>, Arc<Box<[SyntaxError]>>> {
|
||||
) -> ValueResult<(Arc<tt::Subtree>, SyntaxFixupUndoInfo), Arc<Box<[SyntaxError]>>> {
|
||||
) -> (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span) {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
if let Some(EagerCallInfo { arg, .. }) = matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
|
||||
.then(|| loc.eager.as_deref())
|
||||
.flatten()
|
||||
|
||||
if let MacroCallLoc {
|
||||
def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. },
|
||||
kind: MacroCallKind::FnLike { eager: Some(eager), .. },
|
||||
..
|
||||
} = &loc
|
||||
{
|
||||
ValueResult::ok((arg.clone(), SyntaxFixupUndoInfo::NONE))
|
||||
} else {
|
||||
let (parse, map) = parse_with_map(db, loc.kind.file_id());
|
||||
let root = parse.syntax_node();
|
||||
|
||||
let syntax = match loc.kind {
|
||||
MacroCallKind::FnLike { ast_id, .. } => {
|
||||
let dummy_tt = |kind| {
|
||||
(
|
||||
Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter {
|
||||
open: loc.call_site,
|
||||
close: loc.call_site,
|
||||
kind,
|
||||
},
|
||||
token_trees: Box::default(),
|
||||
}),
|
||||
SyntaxFixupUndoInfo::default(),
|
||||
)
|
||||
};
|
||||
|
||||
let node = &ast_id.to_ptr(db).to_node(&root);
|
||||
let offset = node.syntax().text_range().start();
|
||||
let Some(tt) = node.token_tree() else {
|
||||
return ValueResult::new(
|
||||
dummy_tt(tt::DelimiterKind::Invisible),
|
||||
Arc::new(Box::new([SyntaxError::new_at_offset(
|
||||
"missing token tree".to_owned(),
|
||||
offset,
|
||||
)])),
|
||||
);
|
||||
};
|
||||
let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']);
|
||||
let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]);
|
||||
|
||||
let mismatched_delimiters = !matches!(
|
||||
(first, last),
|
||||
(T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}'])
|
||||
);
|
||||
if mismatched_delimiters {
|
||||
// Don't expand malformed (unbalanced) macro invocations. This is
|
||||
// less than ideal, but trying to expand unbalanced macro calls
|
||||
// sometimes produces pathological, deeply nested code which breaks
|
||||
// all kinds of things.
|
||||
//
|
||||
// So instead, we'll return an empty subtree here
|
||||
cov_mark::hit!(issue9358_bad_macro_stack_overflow);
|
||||
|
||||
let kind = match first {
|
||||
_ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible,
|
||||
T!['('] => tt::DelimiterKind::Parenthesis,
|
||||
T!['['] => tt::DelimiterKind::Bracket,
|
||||
T!['{'] => tt::DelimiterKind::Brace,
|
||||
_ => tt::DelimiterKind::Invisible,
|
||||
};
|
||||
return ValueResult::new(
|
||||
dummy_tt(kind),
|
||||
Arc::new(Box::new([SyntaxError::new_at_offset(
|
||||
"mismatched delimiters".to_owned(),
|
||||
offset,
|
||||
)])),
|
||||
);
|
||||
}
|
||||
tt.syntax().clone()
|
||||
}
|
||||
MacroCallKind::Derive { ast_id, .. } => {
|
||||
ast_id.to_ptr(db).to_node(&root).syntax().clone()
|
||||
}
|
||||
MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).to_node(&root).syntax().clone(),
|
||||
};
|
||||
let (mut tt, undo_info) = match loc.kind {
|
||||
MacroCallKind::FnLike { .. } => (
|
||||
mbe::syntax_node_to_token_tree(&syntax, map.as_ref(), loc.call_site),
|
||||
SyntaxFixupUndoInfo::NONE,
|
||||
),
|
||||
MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => {
|
||||
let censor = censor_for_macro_input(&loc, &syntax);
|
||||
let mut fixups = fixup::fixup_syntax(map.as_ref(), &syntax, loc.call_site);
|
||||
fixups.append.retain(|it, _| match it {
|
||||
syntax::NodeOrToken::Node(it) => !censor.contains(it),
|
||||
syntax::NodeOrToken::Token(_) => true,
|
||||
});
|
||||
fixups.remove.extend(censor);
|
||||
{
|
||||
let mut tt = mbe::syntax_node_to_token_tree_modified(
|
||||
&syntax,
|
||||
map.as_ref(),
|
||||
fixups.append.clone(),
|
||||
fixups.remove.clone(),
|
||||
loc.call_site,
|
||||
);
|
||||
reverse_fixups(&mut tt, &fixups.undo_info);
|
||||
}
|
||||
(
|
||||
mbe::syntax_node_to_token_tree_modified(
|
||||
&syntax,
|
||||
map,
|
||||
fixups.append,
|
||||
fixups.remove,
|
||||
loc.call_site,
|
||||
),
|
||||
fixups.undo_info,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
if loc.def.is_proc_macro() {
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.delimiter.kind = tt::DelimiterKind::Invisible;
|
||||
}
|
||||
|
||||
if matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) {
|
||||
match parse.errors() {
|
||||
errors if errors.is_empty() => ValueResult::ok((Arc::new(tt), undo_info)),
|
||||
errors => ValueResult::new(
|
||||
(Arc::new(tt), undo_info),
|
||||
// Box::<[_]>::from(res.errors()), not stable yet
|
||||
Arc::new(errors.to_vec().into_boxed_slice()),
|
||||
),
|
||||
}
|
||||
} else {
|
||||
ValueResult::ok((Arc::new(tt), undo_info))
|
||||
}
|
||||
return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span);
|
||||
}
|
||||
|
||||
let (parse, map) = parse_with_map(db, loc.kind.file_id());
|
||||
let root = parse.syntax_node();
|
||||
|
||||
let (censor, item_node, span) = match loc.kind {
|
||||
MacroCallKind::FnLike { ast_id, .. } => {
|
||||
let node = &ast_id.to_ptr(db).to_node(&root);
|
||||
let path_range = node
|
||||
.path()
|
||||
.map_or_else(|| node.syntax().text_range(), |path| path.syntax().text_range());
|
||||
let span = map.span_for_range(path_range);
|
||||
|
||||
let dummy_tt = |kind| {
|
||||
(
|
||||
Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter { open: span, close: span, kind },
|
||||
token_trees: Box::default(),
|
||||
}),
|
||||
SyntaxFixupUndoInfo::default(),
|
||||
span,
|
||||
)
|
||||
};
|
||||
|
||||
let Some(tt) = node.token_tree() else {
|
||||
return dummy_tt(tt::DelimiterKind::Invisible);
|
||||
};
|
||||
let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']);
|
||||
let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]);
|
||||
|
||||
let mismatched_delimiters = !matches!(
|
||||
(first, last),
|
||||
(T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}'])
|
||||
);
|
||||
if mismatched_delimiters {
|
||||
// Don't expand malformed (unbalanced) macro invocations. This is
|
||||
// less than ideal, but trying to expand unbalanced macro calls
|
||||
// sometimes produces pathological, deeply nested code which breaks
|
||||
// all kinds of things.
|
||||
//
|
||||
// So instead, we'll return an empty subtree here
|
||||
cov_mark::hit!(issue9358_bad_macro_stack_overflow);
|
||||
|
||||
let kind = match first {
|
||||
_ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible,
|
||||
T!['('] => tt::DelimiterKind::Parenthesis,
|
||||
T!['['] => tt::DelimiterKind::Bracket,
|
||||
T!['{'] => tt::DelimiterKind::Brace,
|
||||
_ => tt::DelimiterKind::Invisible,
|
||||
};
|
||||
return dummy_tt(kind);
|
||||
}
|
||||
|
||||
let mut tt = mbe::syntax_node_to_token_tree(tt.syntax(), map.as_ref(), span);
|
||||
if loc.def.is_proc_macro() {
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.delimiter.kind = tt::DelimiterKind::Invisible;
|
||||
}
|
||||
return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span);
|
||||
}
|
||||
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
|
||||
let node = ast_id.to_ptr(db).to_node(&root);
|
||||
let censor_derive_input = censor_derive_input(derive_attr_index, &node);
|
||||
let item_node = node.into();
|
||||
let attr_source = attr_source(derive_attr_index, &item_node);
|
||||
// FIXME: This is wrong, this should point to the path of the derive attribute`
|
||||
let span =
|
||||
map.span_for_range(attr_source.as_ref().and_then(|it| it.path()).map_or_else(
|
||||
|| item_node.syntax().text_range(),
|
||||
|it| it.syntax().text_range(),
|
||||
));
|
||||
(censor_derive_input, item_node, span)
|
||||
}
|
||||
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
|
||||
let node = ast_id.to_ptr(db).to_node(&root);
|
||||
let attr_source = attr_source(invoc_attr_index, &node);
|
||||
let span = map.span_for_range(
|
||||
attr_source
|
||||
.as_ref()
|
||||
.and_then(|it| it.path())
|
||||
.map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()),
|
||||
);
|
||||
(attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span)
|
||||
}
|
||||
};
|
||||
|
||||
let (mut tt, undo_info) = {
|
||||
let syntax = item_node.syntax();
|
||||
let censor_cfg = cfg_process::process_cfg_attrs(syntax, &loc, db).unwrap_or_default();
|
||||
let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span);
|
||||
fixups.append.retain(|it, _| match it {
|
||||
syntax::NodeOrToken::Token(_) => true,
|
||||
it => !censor.contains(it) && !censor_cfg.contains(it),
|
||||
});
|
||||
fixups.remove.extend(censor);
|
||||
fixups.remove.extend(censor_cfg);
|
||||
|
||||
(
|
||||
mbe::syntax_node_to_token_tree_modified(
|
||||
syntax,
|
||||
map,
|
||||
fixups.append,
|
||||
fixups.remove,
|
||||
span,
|
||||
),
|
||||
fixups.undo_info,
|
||||
)
|
||||
};
|
||||
|
||||
if loc.def.is_proc_macro() {
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.delimiter.kind = tt::DelimiterKind::Invisible;
|
||||
}
|
||||
|
||||
(Arc::new(tt), undo_info, span)
|
||||
}
|
||||
|
||||
// FIXME: Censoring info should be calculated by the caller! Namely by name resolution
|
||||
/// Certain macro calls expect some nodes in the input to be preprocessed away, namely:
|
||||
/// - derives expect all `#[derive(..)]` invocations up to the currently invoked one to be stripped
|
||||
/// - attributes expect the invoking attribute to be stripped
|
||||
fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<SyntaxNode> {
|
||||
/// Derives expect all `#[derive(..)]` invocations up to (and including) the currently invoked one to be stripped
|
||||
fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet<SyntaxElement> {
|
||||
// FIXME: handle `cfg_attr`
|
||||
(|| {
|
||||
let censor = match loc.kind {
|
||||
MacroCallKind::FnLike { .. } => return None,
|
||||
MacroCallKind::Derive { derive_attr_index, .. } => {
|
||||
cov_mark::hit!(derive_censoring);
|
||||
ast::Item::cast(node.clone())?
|
||||
.attrs()
|
||||
.take(derive_attr_index.ast_index() + 1)
|
||||
// FIXME, this resolution should not be done syntactically
|
||||
// derive is a proper macro now, no longer builtin
|
||||
// But we do not have resolution at this stage, this means
|
||||
// we need to know about all macro calls for the given ast item here
|
||||
// so we require some kind of mapping...
|
||||
.filter(|attr| attr.simple_name().as_deref() == Some("derive"))
|
||||
.map(|it| it.syntax().clone())
|
||||
.collect()
|
||||
}
|
||||
MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None,
|
||||
MacroCallKind::Attr { invoc_attr_index, .. } => {
|
||||
cov_mark::hit!(attribute_macro_attr_censoring);
|
||||
collect_attrs(&ast::Item::cast(node.clone())?)
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(|x| Either::left(x.1))
|
||||
.map(|attr| attr.syntax().clone())
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
Some(censor)
|
||||
})()
|
||||
.unwrap_or_default()
|
||||
cov_mark::hit!(derive_censoring);
|
||||
collect_attrs(node)
|
||||
.take(derive_attr_index.ast_index() + 1)
|
||||
.filter_map(|(_, attr)| Either::left(attr))
|
||||
// FIXME, this resolution should not be done syntactically
|
||||
// derive is a proper macro now, no longer builtin
|
||||
// But we do not have resolution at this stage, this means
|
||||
// we need to know about all macro calls for the given ast item here
|
||||
// so we require some kind of mapping...
|
||||
.filter(|attr| attr.simple_name().as_deref() == Some("derive"))
|
||||
.map(|it| it.syntax().clone().into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Attributes expect the invoking attribute to be stripped
|
||||
fn attr_source(invoc_attr_index: AttrId, node: &ast::Item) -> Option<ast::Attr> {
|
||||
// FIXME: handle `cfg_attr`
|
||||
cov_mark::hit!(attribute_macro_attr_censoring);
|
||||
collect_attrs(node).nth(invoc_attr_index.ast_index()).and_then(|(_, attr)| Either::left(attr))
|
||||
}
|
||||
|
||||
impl TokenExpander {
|
||||
|
@ -523,74 +523,64 @@ fn macro_expand(
|
|||
) -> ExpandResult<CowArc<tt::Subtree>> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered();
|
||||
|
||||
let ExpandResult { value: tt, mut err } = match loc.def.kind {
|
||||
let (ExpandResult { value: tt, err }, span) = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc),
|
||||
_ => {
|
||||
let ValueResult { value: (macro_arg, undo_info), err } = db.macro_arg(macro_call_id);
|
||||
let format_parse_err = |err: Arc<Box<[SyntaxError]>>| {
|
||||
let mut buf = String::new();
|
||||
for err in &**err {
|
||||
use std::fmt::Write;
|
||||
_ = write!(buf, "{}, ", err);
|
||||
}
|
||||
buf.pop();
|
||||
buf.pop();
|
||||
ExpandError::other(buf)
|
||||
};
|
||||
let (macro_arg, undo_info, span) = db.macro_arg(macro_call_id);
|
||||
|
||||
let arg = &*macro_arg;
|
||||
let res = match loc.def.kind {
|
||||
MacroDefKind::Declarative(id) => {
|
||||
db.decl_macro_expander(loc.def.krate, id).expand(db, arg.clone(), macro_call_id)
|
||||
}
|
||||
MacroDefKind::BuiltIn(it, _) => {
|
||||
it.expand(db, macro_call_id, arg).map_err(Into::into)
|
||||
}
|
||||
// This might look a bit odd, but we do not expand the inputs to eager macros here.
|
||||
// Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
|
||||
// That kind of expansion uses the ast id map of an eager macros input though which goes through
|
||||
// the HirFileId machinery. As eager macro inputs are assigned a macro file id that query
|
||||
// will end up going through here again, whereas we want to just want to inspect the raw input.
|
||||
// As such we just return the input subtree here.
|
||||
MacroDefKind::BuiltInEager(..) if loc.eager.is_none() => {
|
||||
return ExpandResult {
|
||||
value: CowArc::Arc(macro_arg.clone()),
|
||||
err: err.map(format_parse_err),
|
||||
};
|
||||
}
|
||||
MacroDefKind::BuiltInDerive(it, _) => {
|
||||
it.expand(db, macro_call_id, arg).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInEager(it, _) => {
|
||||
it.expand(db, macro_call_id, arg).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(it, _) => {
|
||||
let mut res = it.expand(db, macro_call_id, arg);
|
||||
fixup::reverse_fixups(&mut res.value, &undo_info);
|
||||
res
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
ExpandResult {
|
||||
value: res.value,
|
||||
// if the arg had parse errors, show them instead of the expansion errors
|
||||
err: err.map(format_parse_err).or(res.err),
|
||||
}
|
||||
let res =
|
||||
match loc.def.kind {
|
||||
MacroDefKind::Declarative(id) => db
|
||||
.decl_macro_expander(loc.def.krate, id)
|
||||
.expand(db, arg.clone(), macro_call_id, span),
|
||||
MacroDefKind::BuiltIn(it, _) => {
|
||||
it.expand(db, macro_call_id, arg, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInDerive(it, _) => {
|
||||
it.expand(db, macro_call_id, arg, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInEager(it, _) => {
|
||||
// This might look a bit odd, but we do not expand the inputs to eager macros here.
|
||||
// Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
|
||||
// That kind of expansion uses the ast id map of an eager macros input though which goes through
|
||||
// the HirFileId machinery. As eager macro inputs are assigned a macro file id that query
|
||||
// will end up going through here again, whereas we want to just want to inspect the raw input.
|
||||
// As such we just return the input subtree here.
|
||||
let eager = match &loc.kind {
|
||||
MacroCallKind::FnLike { eager: None, .. } => {
|
||||
return ExpandResult::ok(CowArc::Arc(macro_arg.clone()));
|
||||
}
|
||||
MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let mut res = it.expand(db, macro_call_id, arg, span).map_err(Into::into);
|
||||
|
||||
if let Some(EagerCallInfo { error, .. }) = eager {
|
||||
// FIXME: We should report both errors!
|
||||
res.err = error.clone().or(res.err);
|
||||
}
|
||||
res
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(it, _) => {
|
||||
let mut res = it.expand(db, macro_call_id, arg, span);
|
||||
fixup::reverse_fixups(&mut res.value, &undo_info);
|
||||
res
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
(ExpandResult { value: res.value, err: res.err }, span)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(EagerCallInfo { error, .. }) = loc.eager.as_deref() {
|
||||
// FIXME: We should report both errors!
|
||||
err = error.clone().or(err);
|
||||
}
|
||||
|
||||
// Skip checking token tree limit for include! macro call
|
||||
if !loc.def.is_include() {
|
||||
// Set a hard limit for the expanded tt
|
||||
if let Err(value) = check_tt_count(&tt) {
|
||||
return value.map(|()| {
|
||||
CowArc::Owned(tt::Subtree {
|
||||
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
||||
delimiter: tt::Delimiter::invisible_spanned(span),
|
||||
token_trees: Box::new([]),
|
||||
})
|
||||
});
|
||||
|
@ -600,12 +590,23 @@ fn macro_expand(
|
|||
ExpandResult { value: CowArc::Owned(tt), err }
|
||||
}
|
||||
|
||||
fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
|
||||
let root = db.parse_or_expand(ast.file_id);
|
||||
let ast_id_map = &db.ast_id_map(ast.file_id);
|
||||
let span_map = &db.span_map(ast.file_id);
|
||||
|
||||
let node = ast_id_map.get(ast.value).to_node(&root);
|
||||
let range = ast::HasName::name(&node)
|
||||
.map_or_else(|| node.syntax().text_range(), |name| name.syntax().text_range());
|
||||
span_map.span_for_range(range)
|
||||
}
|
||||
|
||||
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let (macro_arg, undo_info) = db.macro_arg(id).value;
|
||||
let (macro_arg, undo_info, span) = db.macro_arg(id);
|
||||
|
||||
let expander = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(expander, ..) => expander,
|
||||
let (expander, ast) = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
|
@ -614,22 +615,25 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
|
|||
_ => None,
|
||||
};
|
||||
|
||||
let ExpandResult { value: mut tt, err } = expander.expand(
|
||||
db,
|
||||
loc.def.krate,
|
||||
loc.krate,
|
||||
¯o_arg,
|
||||
attr_arg,
|
||||
span_with_def_site_ctxt(db, loc.def.span, id),
|
||||
span_with_call_site_ctxt(db, loc.def.span, id),
|
||||
span_with_mixed_site_ctxt(db, loc.def.span, id),
|
||||
);
|
||||
let ExpandResult { value: mut tt, err } = {
|
||||
let span = db.proc_macro_span(ast);
|
||||
expander.expand(
|
||||
db,
|
||||
loc.def.krate,
|
||||
loc.krate,
|
||||
¯o_arg,
|
||||
attr_arg,
|
||||
span_with_def_site_ctxt(db, span, id),
|
||||
span_with_call_site_ctxt(db, span, id),
|
||||
span_with_mixed_site_ctxt(db, span, id),
|
||||
)
|
||||
};
|
||||
|
||||
// Set a hard limit for the expanded tt
|
||||
if let Err(value) = check_tt_count(&tt) {
|
||||
return value.map(|()| {
|
||||
Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
||||
delimiter: tt::Delimiter::invisible_spanned(span),
|
||||
token_trees: Box::new([]),
|
||||
})
|
||||
});
|
||||
|
|
|
@ -29,6 +29,7 @@ impl DeclarativeMacroExpander {
|
|||
db: &dyn ExpandDatabase,
|
||||
tt: tt::Subtree,
|
||||
call_id: MacroCallId,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let loc = db.lookup_intern_macro_call(call_id);
|
||||
let toolchain = db.toolchain(loc.def.krate);
|
||||
|
@ -45,7 +46,7 @@ impl DeclarativeMacroExpander {
|
|||
});
|
||||
match self.mac.err() {
|
||||
Some(_) => ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }),
|
||||
tt::Subtree::empty(tt::DelimSpan { open: span, close: span }),
|
||||
ExpandError::MacroDefinition,
|
||||
),
|
||||
None => self
|
||||
|
@ -54,7 +55,7 @@ impl DeclarativeMacroExpander {
|
|||
&tt,
|
||||
|s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
|
||||
new_meta_vars,
|
||||
loc.call_site,
|
||||
span,
|
||||
)
|
||||
.map_err(Into::into),
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
//!
|
||||
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
|
||||
use base_db::CrateId;
|
||||
use span::Span;
|
||||
use span::SyntaxContextId;
|
||||
use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent};
|
||||
use triomphe::Arc;
|
||||
|
||||
|
@ -27,22 +27,20 @@ use crate::{
|
|||
ast::{self, AstNode},
|
||||
db::ExpandDatabase,
|
||||
mod_path::ModPath,
|
||||
EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, Intern,
|
||||
AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, Intern,
|
||||
MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
|
||||
};
|
||||
|
||||
pub fn expand_eager_macro_input(
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
macro_call: InFile<ast::MacroCall>,
|
||||
macro_call: &ast::MacroCall,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
def: MacroDefId,
|
||||
call_site: Span,
|
||||
call_site: SyntaxContextId,
|
||||
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
) -> ExpandResult<Option<MacroCallId>> {
|
||||
let ast_map = db.ast_id_map(macro_call.file_id);
|
||||
// the expansion which the ast id map is built upon has no whitespace, so the offsets are wrong as macro_call is from the token tree that has whitespace!
|
||||
let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value));
|
||||
let expand_to = ExpandTo::from_call_site(¯o_call.value);
|
||||
let expand_to = ExpandTo::from_call_site(macro_call);
|
||||
|
||||
// Note:
|
||||
// When `lazy_expand` is called, its *parent* file must already exist.
|
||||
|
@ -51,11 +49,11 @@ pub fn expand_eager_macro_input(
|
|||
let arg_id = MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
eager: None,
|
||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
|
||||
call_site,
|
||||
kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None },
|
||||
ctxt: call_site,
|
||||
}
|
||||
.intern(db);
|
||||
let (_, _, span) = db.macro_arg(arg_id);
|
||||
let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } =
|
||||
db.parse_macro_expansion(arg_id.as_macro_file());
|
||||
|
||||
|
@ -82,16 +80,24 @@ pub fn expand_eager_macro_input(
|
|||
return ExpandResult { value: None, err };
|
||||
};
|
||||
|
||||
let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, call_site);
|
||||
let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, span);
|
||||
|
||||
subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible;
|
||||
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
eager: Some(Arc::new(EagerCallInfo { arg: Arc::new(subtree), arg_id, error: err.clone() })),
|
||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
|
||||
call_site,
|
||||
kind: MacroCallKind::FnLike {
|
||||
ast_id,
|
||||
expand_to,
|
||||
eager: Some(Arc::new(EagerCallInfo {
|
||||
arg: Arc::new(subtree),
|
||||
arg_id,
|
||||
error: err.clone(),
|
||||
span,
|
||||
})),
|
||||
},
|
||||
ctxt: call_site,
|
||||
};
|
||||
|
||||
ExpandResult { value: Some(loc.intern(db)), err }
|
||||
|
@ -100,15 +106,18 @@ pub fn expand_eager_macro_input(
|
|||
fn lazy_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
def: &MacroDefId,
|
||||
macro_call: InFile<ast::MacroCall>,
|
||||
macro_call: &ast::MacroCall,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
krate: CrateId,
|
||||
call_site: Span,
|
||||
call_site: SyntaxContextId,
|
||||
) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<ExpansionSpanMap>)> {
|
||||
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value);
|
||||
|
||||
let expand_to = ExpandTo::from_call_site(¯o_call.value);
|
||||
let ast_id = macro_call.with_value(ast_id);
|
||||
let id = def.as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id, expand_to }, call_site);
|
||||
let expand_to = ExpandTo::from_call_site(macro_call);
|
||||
let id = def.make_call(
|
||||
db,
|
||||
krate,
|
||||
MacroCallKind::FnLike { ast_id, expand_to, eager: None },
|
||||
call_site,
|
||||
);
|
||||
let macro_file = id.as_macro_file();
|
||||
|
||||
db.parse_macro_expansion(macro_file)
|
||||
|
@ -122,7 +131,7 @@ fn eager_macro_recur(
|
|||
mut offset: TextSize,
|
||||
curr: InFile<SyntaxNode>,
|
||||
krate: CrateId,
|
||||
call_site: Span,
|
||||
call_site: SyntaxContextId,
|
||||
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
) -> ExpandResult<Option<(SyntaxNode, TextSize)>> {
|
||||
let original = curr.value.clone_for_update();
|
||||
|
@ -172,12 +181,14 @@ fn eager_macro_recur(
|
|||
continue;
|
||||
}
|
||||
};
|
||||
let ast_id = db.ast_id_map(curr.file_id).ast_id(&call);
|
||||
let ExpandResult { value, err } = match def.kind {
|
||||
MacroDefKind::BuiltInEager(..) => {
|
||||
let ExpandResult { value, err } = expand_eager_macro_input(
|
||||
db,
|
||||
krate,
|
||||
curr.with_value(call.clone()),
|
||||
&call,
|
||||
curr.with_value(ast_id),
|
||||
def,
|
||||
call_site,
|
||||
macro_resolver,
|
||||
|
@ -207,7 +218,7 @@ fn eager_macro_recur(
|
|||
| MacroDefKind::BuiltInDerive(..)
|
||||
| MacroDefKind::ProcMacro(..) => {
|
||||
let ExpandResult { value: (parse, tm), err } =
|
||||
lazy_expand(db, &def, curr.with_value(call.clone()), krate, call_site);
|
||||
lazy_expand(db, &def, &call, curr.with_value(ast_id), krate, call_site);
|
||||
|
||||
// replace macro inside
|
||||
let ExpandResult { value, err: error } = eager_macro_recur(
|
||||
|
|
|
@ -10,7 +10,7 @@ use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
|
|||
|
||||
use crate::{
|
||||
db::{self, ExpandDatabase},
|
||||
map_node_range_up, span_for_offset, MacroFileIdExt,
|
||||
map_node_range_up, map_node_range_up_rooted, span_for_offset, MacroFileIdExt,
|
||||
};
|
||||
|
||||
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
|
||||
|
@ -38,6 +38,9 @@ impl<N: AstIdNode> AstId<N> {
|
|||
pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
|
||||
self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
|
||||
}
|
||||
pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange {
|
||||
self.to_ptr(db).text_range()
|
||||
}
|
||||
pub fn to_in_file_node(&self, db: &dyn ExpandDatabase) -> crate::InFile<N> {
|
||||
crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)))
|
||||
}
|
||||
|
@ -49,6 +52,9 @@ impl<N: AstIdNode> AstId<N> {
|
|||
pub type ErasedAstId = crate::InFile<ErasedFileAstId>;
|
||||
|
||||
impl ErasedAstId {
|
||||
pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange {
|
||||
self.to_ptr(db).text_range()
|
||||
}
|
||||
pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> SyntaxNodePtr {
|
||||
db.ast_id_map(self.file_id).get_erased(self.value)
|
||||
}
|
||||
|
@ -173,24 +179,8 @@ impl InFile<&SyntaxNode> {
|
|||
///
|
||||
/// For attributes and derives, this will point back to the attribute only.
|
||||
/// For the entire item use [`InFile::original_file_range_full`].
|
||||
pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
if let Some((res, ctxt)) =
|
||||
map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range())
|
||||
{
|
||||
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
|
||||
// keep pre-token map rewrite behaviour.
|
||||
if ctxt.is_root() {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
// Fall back to whole macro call.
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
loc.kind.original_call_range(db)
|
||||
}
|
||||
}
|
||||
pub fn original_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange {
|
||||
self.map(SyntaxNode::text_range).original_node_file_range_rooted(db)
|
||||
}
|
||||
|
||||
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
||||
|
@ -198,23 +188,7 @@ impl InFile<&SyntaxNode> {
|
|||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> FileRange {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
if let Some((res, ctxt)) =
|
||||
map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range())
|
||||
{
|
||||
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
|
||||
// keep pre-token map rewrite behaviour.
|
||||
if ctxt.is_root() {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
// Fall back to whole macro call.
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
loc.kind.original_call_range_with_body(db)
|
||||
}
|
||||
}
|
||||
self.map(SyntaxNode::text_range).original_node_file_range_with_macro_call_body(db)
|
||||
}
|
||||
|
||||
/// Attempts to map the syntax node back up its macro calls.
|
||||
|
@ -222,17 +196,10 @@ impl InFile<&SyntaxNode> {
|
|||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> Option<(FileRange, SyntaxContextId)> {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
Some((FileRange { file_id, range: self.value.text_range() }, SyntaxContextId::ROOT))
|
||||
}
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range())
|
||||
}
|
||||
}
|
||||
self.map(SyntaxNode::text_range).original_node_file_range_opt(db)
|
||||
}
|
||||
|
||||
pub fn original_syntax_node(
|
||||
pub fn original_syntax_node_rooted(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> Option<InRealFile<SyntaxNode>> {
|
||||
|
@ -242,25 +209,21 @@ impl InFile<&SyntaxNode> {
|
|||
HirFileIdRepr::FileId(file_id) => {
|
||||
return Some(InRealFile { file_id, value: self.value.clone() })
|
||||
}
|
||||
HirFileIdRepr::MacroFile(m) => m,
|
||||
HirFileIdRepr::MacroFile(m) if m.is_attr_macro(db) => m,
|
||||
_ => return None,
|
||||
};
|
||||
if !file_id.is_attr_macro(db) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (FileRange { file_id, range }, ctx) =
|
||||
map_node_range_up(db, &db.expansion_span_map(file_id), self.value.text_range())?;
|
||||
let FileRange { file_id, range } =
|
||||
map_node_range_up_rooted(db, &db.expansion_span_map(file_id), self.value.text_range())?;
|
||||
|
||||
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
|
||||
// keep pre-token map rewrite behavior.
|
||||
if !ctx.is_root() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let anc = db.parse(file_id).syntax_node().covering_element(range);
|
||||
let kind = self.value.kind();
|
||||
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes?
|
||||
let value = anc.ancestors().find(|it| it.kind() == kind)?;
|
||||
let value = db
|
||||
.parse(file_id)
|
||||
.syntax_node()
|
||||
.covering_element(range)
|
||||
.ancestors()
|
||||
.take_while(|it| it.text_range() == range)
|
||||
.find(|it| it.kind() == kind)?;
|
||||
Some(InRealFile::new(file_id, value))
|
||||
}
|
||||
}
|
||||
|
@ -355,8 +318,8 @@ impl InFile<TextRange> {
|
|||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) {
|
||||
Some((it, SyntaxContextId::ROOT)) => it,
|
||||
match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
|
||||
Some(it) => it,
|
||||
_ => {
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
loc.kind.original_call_range(db)
|
||||
|
@ -366,6 +329,24 @@ impl InFile<TextRange> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn original_node_file_range_with_macro_call_body(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> FileRange {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
|
||||
Some(it) => it,
|
||||
_ => {
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
loc.kind.original_call_range_with_body(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn original_node_file_range_opt(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
|
@ -395,18 +376,12 @@ impl<N: AstNode> InFile<N> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let (FileRange { file_id, range }, ctx) = map_node_range_up(
|
||||
let FileRange { file_id, range } = map_node_range_up_rooted(
|
||||
db,
|
||||
&db.expansion_span_map(file_id),
|
||||
self.value.syntax().text_range(),
|
||||
)?;
|
||||
|
||||
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
|
||||
// keep pre-token map rewrite behaviour.
|
||||
if !ctx.is_root() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes?
|
||||
let anc = db.parse(file_id).syntax_node().covering_element(range);
|
||||
let value = anc.ancestors().find_map(N::cast)?;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::SmallVec;
|
||||
use span::{ErasedFileAstId, Span, SpanAnchor, SpanData, FIXUP_ERASED_FILE_AST_ID_MARKER};
|
||||
use span::{ErasedFileAstId, Span, SpanAnchor, FIXUP_ERASED_FILE_AST_ID_MARKER};
|
||||
use stdx::never;
|
||||
use syntax::{
|
||||
ast::{self, AstNode, HasLoopBody},
|
||||
|
@ -23,7 +23,7 @@ use crate::{
|
|||
#[derive(Debug, Default)]
|
||||
pub(crate) struct SyntaxFixups {
|
||||
pub(crate) append: FxHashMap<SyntaxElement, Vec<Leaf>>,
|
||||
pub(crate) remove: FxHashSet<SyntaxNode>,
|
||||
pub(crate) remove: FxHashSet<SyntaxElement>,
|
||||
pub(crate) undo_info: SyntaxFixupUndoInfo,
|
||||
}
|
||||
|
||||
|
@ -51,13 +51,13 @@ pub(crate) fn fixup_syntax(
|
|||
call_site: Span,
|
||||
) -> SyntaxFixups {
|
||||
let mut append = FxHashMap::<SyntaxElement, _>::default();
|
||||
let mut remove = FxHashSet::<SyntaxNode>::default();
|
||||
let mut remove = FxHashSet::<SyntaxElement>::default();
|
||||
let mut preorder = node.preorder();
|
||||
let mut original = Vec::new();
|
||||
let dummy_range = FIXUP_DUMMY_RANGE;
|
||||
let fake_span = |range| {
|
||||
let span = span_map.span_for_range(range);
|
||||
SpanData {
|
||||
Span {
|
||||
range: dummy_range,
|
||||
anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor },
|
||||
ctx: span.ctx,
|
||||
|
@ -68,7 +68,7 @@ pub(crate) fn fixup_syntax(
|
|||
|
||||
let node_range = node.text_range();
|
||||
if can_handle_error(&node) && has_error_to_handle(&node) {
|
||||
remove.insert(node.clone());
|
||||
remove.insert(node.clone().into());
|
||||
// the node contains an error node, we have to completely replace it by something valid
|
||||
let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site);
|
||||
let idx = original.len() as u32;
|
||||
|
@ -76,7 +76,7 @@ pub(crate) fn fixup_syntax(
|
|||
let span = span_map.span_for_range(node_range);
|
||||
let replacement = Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: SpanData {
|
||||
span: Span {
|
||||
range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END),
|
||||
anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor },
|
||||
ctx: span.ctx,
|
||||
|
@ -305,8 +305,8 @@ pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo)
|
|||
tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
|| tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
) {
|
||||
tt.delimiter.close = SpanData::DUMMY;
|
||||
tt.delimiter.open = SpanData::DUMMY;
|
||||
tt.delimiter.close = Span::DUMMY;
|
||||
tt.delimiter.open = Span::DUMMY;
|
||||
}
|
||||
reverse_fixups_(tt, undo_info);
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ pub(super) fn apply_mark(
|
|||
return apply_mark_internal(db, ctxt, call_id, transparency);
|
||||
}
|
||||
|
||||
let call_site_ctxt = db.lookup_intern_macro_call(call_id).call_site.ctx;
|
||||
let call_site_ctxt = db.lookup_intern_macro_call(call_id).ctxt;
|
||||
let mut call_site_ctxt = if transparency == Transparency::SemiTransparent {
|
||||
call_site_ctxt.normalize_to_macros_2_0(db)
|
||||
} else {
|
||||
|
@ -205,11 +205,10 @@ pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String {
|
|||
let id = e.key;
|
||||
let expn_data = e.value.as_ref().unwrap();
|
||||
s.push_str(&format!(
|
||||
"\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}",
|
||||
"\n{:?}: parent: {:?}, call_site_ctxt: {:?}, kind: {:?}",
|
||||
id,
|
||||
expn_data.kind.file_id(),
|
||||
expn_data.call_site,
|
||||
SyntaxContextId::ROOT, // FIXME expn_data.def_site,
|
||||
expn_data.ctxt,
|
||||
expn_data.kind.descr(),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -22,16 +22,19 @@ pub mod proc_macro;
|
|||
pub mod quote;
|
||||
pub mod span_map;
|
||||
|
||||
mod cfg_process;
|
||||
mod fixup;
|
||||
|
||||
use attrs::collect_attrs;
|
||||
use rustc_hash::FxHashMap;
|
||||
use triomphe::Arc;
|
||||
|
||||
use std::{fmt, hash::Hash};
|
||||
|
||||
use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId};
|
||||
use either::Either;
|
||||
use span::{ErasedFileAstId, FileRange, HirFileIdRepr, Span, SyntaxContextData, SyntaxContextId};
|
||||
use span::{
|
||||
ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId,
|
||||
};
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
SyntaxNode, SyntaxToken, TextRange, TextSize,
|
||||
|
@ -167,13 +170,8 @@ impl fmt::Display for ExpandError {
|
|||
pub struct MacroCallLoc {
|
||||
pub def: MacroDefId,
|
||||
pub krate: CrateId,
|
||||
/// Some if this is a macro call for an eager macro. Note that this is `None`
|
||||
/// for the eager input macro file.
|
||||
// FIXME: This is being interned, subtrees can vary quickly differ just slightly causing
|
||||
// leakage problems here
|
||||
eager: Option<Arc<EagerCallInfo>>,
|
||||
pub kind: MacroCallKind,
|
||||
pub call_site: Span,
|
||||
pub ctxt: SyntaxContextId,
|
||||
}
|
||||
impl_intern_value_trivial!(MacroCallLoc);
|
||||
|
||||
|
@ -184,7 +182,6 @@ pub struct MacroDefId {
|
|||
pub kind: MacroDefKind,
|
||||
pub local_inner: bool,
|
||||
pub allow_internal_unsafe: bool,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -204,6 +201,8 @@ pub struct EagerCallInfo {
|
|||
/// Call id of the eager macro's input file (this is the macro file for its fully expanded input).
|
||||
arg_id: MacroCallId,
|
||||
error: Option<ExpandError>,
|
||||
/// TODO: Doc
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -211,6 +210,11 @@ pub enum MacroCallKind {
|
|||
FnLike {
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
expand_to: ExpandTo,
|
||||
/// Some if this is a macro call for an eager macro. Note that this is `None`
|
||||
/// for the eager input macro file.
|
||||
// FIXME: This is being interned, subtrees can vary quickly differ just slightly causing
|
||||
// leakage problems here
|
||||
eager: Option<Arc<EagerCallInfo>>,
|
||||
},
|
||||
Derive {
|
||||
ast_id: AstId<ast::Adt>,
|
||||
|
@ -272,7 +276,7 @@ impl HirFileIdExt for HirFileId {
|
|||
HirFileIdRepr::MacroFile(file) => {
|
||||
let loc = db.lookup_intern_macro_call(file.macro_call_id);
|
||||
if loc.def.is_include() {
|
||||
if let Some(eager) = &loc.eager {
|
||||
if let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind {
|
||||
if let Ok(it) = builtin_fn_macro::include_input_to_file_id(
|
||||
db,
|
||||
file.macro_call_id,
|
||||
|
@ -319,6 +323,9 @@ impl HirFileIdExt for HirFileId {
|
|||
}
|
||||
|
||||
pub trait MacroFileIdExt {
|
||||
fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool;
|
||||
fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool;
|
||||
fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option<MacroCallId>;
|
||||
fn expansion_level(self, db: &dyn ExpandDatabase) -> u32;
|
||||
/// If this is a macro call, returns the syntax node of the call.
|
||||
fn call_node(self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode>;
|
||||
|
@ -385,31 +392,47 @@ impl MacroFileIdExt for MacroFileId {
|
|||
db.lookup_intern_macro_call(self.macro_call_id).def.is_include()
|
||||
}
|
||||
|
||||
fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool {
|
||||
db.lookup_intern_macro_call(self.macro_call_id).def.is_include_like()
|
||||
}
|
||||
|
||||
fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool {
|
||||
db.lookup_intern_macro_call(self.macro_call_id).def.is_env_or_option_env()
|
||||
}
|
||||
|
||||
fn is_eager(&self, db: &dyn ExpandDatabase) -> bool {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
|
||||
let loc = db.lookup_intern_macro_call(self.macro_call_id);
|
||||
matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
|
||||
}
|
||||
|
||||
fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option<MacroCallId> {
|
||||
let loc = db.lookup_intern_macro_call(self.macro_call_id);
|
||||
match &loc.kind {
|
||||
MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_attr_macro(&self, db: &dyn ExpandDatabase) -> bool {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
|
||||
let loc = db.lookup_intern_macro_call(self.macro_call_id);
|
||||
matches!(loc.kind, MacroCallKind::Attr { .. })
|
||||
}
|
||||
|
||||
fn is_derive_attr_pseudo_expansion(&self, db: &dyn ExpandDatabase) -> bool {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
|
||||
let loc = db.lookup_intern_macro_call(self.macro_call_id);
|
||||
loc.def.is_attribute_derive()
|
||||
}
|
||||
}
|
||||
|
||||
impl MacroDefId {
|
||||
pub fn as_lazy_macro(
|
||||
pub fn make_call(
|
||||
self,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
kind: MacroCallKind,
|
||||
call_site: Span,
|
||||
ctxt: SyntaxContextId,
|
||||
) -> MacroCallId {
|
||||
MacroCallLoc { def: self, krate, eager: None, kind, call_site }.intern(db)
|
||||
MacroCallLoc { def: self, krate, kind, ctxt }.intern(db)
|
||||
}
|
||||
|
||||
pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> {
|
||||
|
@ -474,6 +497,14 @@ impl MacroDefId {
|
|||
pub fn is_include(&self) -> bool {
|
||||
matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include())
|
||||
}
|
||||
|
||||
pub fn is_include_like(&self) -> bool {
|
||||
matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include_like())
|
||||
}
|
||||
|
||||
pub fn is_env_or_option_env(&self) -> bool {
|
||||
matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_env_or_option_env())
|
||||
}
|
||||
}
|
||||
|
||||
impl MacroCallLoc {
|
||||
|
@ -531,7 +562,7 @@ impl MacroCallLoc {
|
|||
macro_call_id: MacroCallId,
|
||||
) -> Option<FileId> {
|
||||
if self.def.is_include() {
|
||||
if let Some(eager) = &self.eager {
|
||||
if let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind {
|
||||
if let Ok(it) =
|
||||
builtin_fn_macro::include_input_to_file_id(db, macro_call_id, &eager.arg)
|
||||
{
|
||||
|
@ -655,7 +686,7 @@ impl MacroCallKind {
|
|||
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
|
||||
// FIXME: can be expensive to create, we should check the use sites and maybe replace them with
|
||||
// simpler function calls if the map is only used once
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ExpansionInfo {
|
||||
pub expanded: InMacroFile<SyntaxNode>,
|
||||
/// The argument TokenTree or item for attributes
|
||||
|
@ -683,6 +714,24 @@ impl ExpansionInfo {
|
|||
}
|
||||
|
||||
/// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
|
||||
///
|
||||
/// Note this does a linear search through the entire backing vector of the spanmap.
|
||||
pub fn map_range_down_exact(
|
||||
&self,
|
||||
span: Span,
|
||||
) -> Option<InMacroFile<impl Iterator<Item = SyntaxToken> + '_>> {
|
||||
let tokens = self
|
||||
.exp_map
|
||||
.ranges_with_span_exact(span)
|
||||
.flat_map(move |range| self.expanded.value.covering_element(range).into_token());
|
||||
|
||||
Some(InMacroFile::new(self.expanded.file_id, tokens))
|
||||
}
|
||||
|
||||
/// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
|
||||
/// Unlike [`map_range_down_exact`], this will consider spans that contain the given span.
|
||||
///
|
||||
/// Note this does a linear search through the entire backing vector of the spanmap.
|
||||
pub fn map_range_down(
|
||||
&self,
|
||||
span: Span,
|
||||
|
@ -739,7 +788,7 @@ impl ExpansionInfo {
|
|||
InFile::new(
|
||||
self.arg.file_id,
|
||||
arg_map
|
||||
.ranges_with_span(span)
|
||||
.ranges_with_span_exact(span)
|
||||
.filter(|range| range.intersect(arg_range).is_some())
|
||||
.collect(),
|
||||
)
|
||||
|
@ -757,7 +806,7 @@ impl ExpansionInfo {
|
|||
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
|
||||
let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
|
||||
|
||||
let (macro_arg, _) = db.macro_arg(macro_file.macro_call_id).value;
|
||||
let (macro_arg, _, _) = db.macro_arg(macro_file.macro_call_id);
|
||||
|
||||
let def = loc.def.ast_id().left().and_then(|id| {
|
||||
let def_tt = match id.to_node(db) {
|
||||
|
@ -793,7 +842,34 @@ impl ExpansionInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// Maps up the text range out of the expansion hierarchy back into the original file its from only
|
||||
/// considering the root spans contained.
|
||||
/// Unlike [`map_node_range_up`], this will not return `None` if any anchors or syntax contexts differ.
|
||||
pub fn map_node_range_up_rooted(
|
||||
db: &dyn ExpandDatabase,
|
||||
exp_map: &ExpansionSpanMap,
|
||||
range: TextRange,
|
||||
) -> Option<FileRange> {
|
||||
let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root());
|
||||
let Span { range, anchor, ctx: _ } = spans.next()?;
|
||||
let mut start = range.start();
|
||||
let mut end = range.end();
|
||||
|
||||
for span in spans {
|
||||
if span.anchor != anchor {
|
||||
return None;
|
||||
}
|
||||
start = start.min(span.range.start());
|
||||
end = end.max(span.range.end());
|
||||
}
|
||||
let anchor_offset =
|
||||
db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start();
|
||||
Some(FileRange { file_id: anchor.file_id, range: TextRange::new(start, end) + anchor_offset })
|
||||
}
|
||||
|
||||
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
|
||||
///
|
||||
/// this will return `None` if any anchors or syntax contexts differ.
|
||||
pub fn map_node_range_up(
|
||||
db: &dyn ExpandDatabase,
|
||||
exp_map: &ExpansionSpanMap,
|
||||
|
@ -819,6 +895,29 @@ pub fn map_node_range_up(
|
|||
))
|
||||
}
|
||||
|
||||
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
|
||||
/// This version will aggregate the ranges of all spans with the same anchor and syntax context.
|
||||
pub fn map_node_range_up_aggregated(
|
||||
db: &dyn ExpandDatabase,
|
||||
exp_map: &ExpansionSpanMap,
|
||||
range: TextRange,
|
||||
) -> FxHashMap<(SpanAnchor, SyntaxContextId), TextRange> {
|
||||
let mut map = FxHashMap::default();
|
||||
for span in exp_map.spans_for_range(range) {
|
||||
let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range);
|
||||
*range = TextRange::new(
|
||||
range.start().min(span.range.start()),
|
||||
range.end().max(span.range.end()),
|
||||
);
|
||||
}
|
||||
for ((anchor, _), range) in &mut map {
|
||||
let anchor_offset =
|
||||
db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start();
|
||||
*range += anchor_offset;
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
/// Looks up the span at the given offset.
|
||||
pub fn span_for_offset(
|
||||
db: &dyn ExpandDatabase,
|
||||
|
|
|
@ -266,10 +266,11 @@ mod tests {
|
|||
|
||||
let quoted = quote!(DUMMY =>#a);
|
||||
assert_eq!(quoted.to_string(), "hello");
|
||||
let t = format!("{quoted:?}");
|
||||
let t = format!("{quoted:#?}");
|
||||
expect![[r#"
|
||||
SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }
|
||||
IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t);
|
||||
SUBTREE $$ 937550:0@0..0#0 937550:0@0..0#0
|
||||
IDENT hello 937550:0@0..0#0"#]]
|
||||
.assert_eq(&t);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
//! Span maps for real files and macro expansions.
|
||||
use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span};
|
||||
use syntax::{AstNode, TextRange};
|
||||
|
||||
use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span, SyntaxContextId};
|
||||
use stdx::TupleExt;
|
||||
use syntax::{ast, AstNode, TextRange};
|
||||
use triomphe::Arc;
|
||||
|
||||
pub use span::RealSpanMap;
|
||||
|
||||
use crate::db::ExpandDatabase;
|
||||
use crate::{attrs::collect_attrs, db::ExpandDatabase};
|
||||
|
||||
pub type ExpansionSpanMap = span::SpanMap<Span>;
|
||||
pub type ExpansionSpanMap = span::SpanMap<SyntaxContextId>;
|
||||
|
||||
/// Spanmap for a macro file or a real file
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -82,13 +84,54 @@ pub(crate) fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc<Rea
|
|||
let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)];
|
||||
let ast_id_map = db.ast_id_map(file_id.into());
|
||||
let tree = db.parse(file_id).tree();
|
||||
// FIXME: Descend into modules and other item containing items that are not annotated with attributes
|
||||
// and allocate pairs for those as well. This gives us finer grained span anchors resulting in
|
||||
// better incrementality
|
||||
pairs.extend(
|
||||
tree.items()
|
||||
.map(|item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())),
|
||||
);
|
||||
// This is an incrementality layer. Basically we can't use absolute ranges for our spans as that
|
||||
// would mean we'd invalidate everything whenever we type. So instead we make the text ranges
|
||||
// relative to some AstIds reducing the risk of invalidation as typing somewhere no longer
|
||||
// affects all following spans in the file.
|
||||
// There is some stuff to bear in mind here though, for one, the more "anchors" we create, the
|
||||
// easier it gets to invalidate things again as spans are as stable as their anchor's ID.
|
||||
// The other problem is proc-macros. Proc-macros have a `Span::join` api that allows them
|
||||
// to join two spans that come from the same file. rust-analyzer's proc-macro server
|
||||
// can only join two spans if they belong to the same anchor though, as the spans are relative
|
||||
// to that anchor. To do cross anchor joining we'd need to access to the ast id map to resolve
|
||||
// them again, something we might get access to in the future. But even then, proc-macros doing
|
||||
// this kind of joining makes them as stable as the AstIdMap (which is basically changing on
|
||||
// every input of the file)…
|
||||
|
||||
let item_to_entry =
|
||||
|item: ast::Item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase());
|
||||
// Top level items make for great anchors as they are the most stable and a decent boundary
|
||||
pairs.extend(tree.items().map(item_to_entry));
|
||||
// Unfortunately, assoc items are very common in Rust, so descend into those as well and make
|
||||
// them anchors too, but only if they have no attributes attached, as those might be proc-macros
|
||||
// and using different anchors inside of them will prevent spans from being joinable.
|
||||
tree.items().for_each(|item| match &item {
|
||||
ast::Item::ExternBlock(it)
|
||||
if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) =>
|
||||
{
|
||||
if let Some(extern_item_list) = it.extern_item_list() {
|
||||
pairs.extend(
|
||||
extern_item_list.extern_items().map(ast::Item::from).map(item_to_entry),
|
||||
);
|
||||
}
|
||||
}
|
||||
ast::Item::Impl(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => {
|
||||
if let Some(assoc_item_list) = it.assoc_item_list() {
|
||||
pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry));
|
||||
}
|
||||
}
|
||||
ast::Item::Module(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => {
|
||||
if let Some(item_list) = it.item_list() {
|
||||
pairs.extend(item_list.items().map(item_to_entry));
|
||||
}
|
||||
}
|
||||
ast::Item::Trait(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => {
|
||||
if let Some(assoc_item_list) = it.assoc_item_list() {
|
||||
pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
|
||||
Arc::new(RealSpanMap::from_file(
|
||||
file_id,
|
||||
|
|
|
@ -23,10 +23,10 @@ oorandom = "11.1.3"
|
|||
tracing.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
scoped-tls = "1.0.0"
|
||||
chalk-solve = { version = "0.96.0", default-features = false }
|
||||
chalk-ir = "0.96.0"
|
||||
chalk-recursive = { version = "0.96.0", default-features = false }
|
||||
chalk-derive = "0.96.0"
|
||||
chalk-solve.workspace = true
|
||||
chalk-ir.workspace = true
|
||||
chalk-recursive.workspace = true
|
||||
chalk-derive.workspace = true
|
||||
la-arena.workspace = true
|
||||
once_cell = "1.17.0"
|
||||
triomphe.workspace = true
|
||||
|
|
|
@ -113,7 +113,7 @@ pub(crate) fn autoderef_step(
|
|||
ty: Ty,
|
||||
explicit: bool,
|
||||
) -> Option<(AutoderefKind, Ty)> {
|
||||
if let Some(derefed) = builtin_deref(table, &ty, explicit) {
|
||||
if let Some(derefed) = builtin_deref(table.db, &ty, explicit) {
|
||||
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
|
||||
} else {
|
||||
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
|
||||
|
@ -121,7 +121,7 @@ pub(crate) fn autoderef_step(
|
|||
}
|
||||
|
||||
pub(crate) fn builtin_deref<'ty>(
|
||||
table: &mut InferenceTable<'_>,
|
||||
db: &dyn HirDatabase,
|
||||
ty: &'ty Ty,
|
||||
explicit: bool,
|
||||
) -> Option<&'ty Ty> {
|
||||
|
@ -129,7 +129,7 @@ pub(crate) fn builtin_deref<'ty>(
|
|||
TyKind::Ref(.., ty) => Some(ty),
|
||||
TyKind::Raw(.., ty) if explicit => Some(ty),
|
||||
&TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => {
|
||||
if crate::lang_items::is_box(table.db, adt) {
|
||||
if crate::lang_items::is_box(db, adt) {
|
||||
substs.at(Interner, 0).ty(Interner)
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -22,7 +22,7 @@ mod pat;
|
|||
mod path;
|
||||
pub(crate) mod unify;
|
||||
|
||||
use std::{convert::identity, ops::Index};
|
||||
use std::{convert::identity, iter, ops::Index};
|
||||
|
||||
use chalk_ir::{
|
||||
cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
|
||||
|
@ -777,7 +777,15 @@ impl<'a> InferenceContext<'a> {
|
|||
|
||||
param_tys.push(va_list_ty)
|
||||
}
|
||||
for (ty, pat) in param_tys.into_iter().zip(self.body.params.iter()) {
|
||||
let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.new_type_var()));
|
||||
if let Some(self_param) = self.body.self_param {
|
||||
if let Some(ty) = param_tys.next() {
|
||||
let ty = self.insert_type_vars(ty);
|
||||
let ty = self.normalize_associated_types_in(ty);
|
||||
self.write_binding_ty(self_param, ty);
|
||||
}
|
||||
}
|
||||
for (ty, pat) in param_tys.zip(&*self.body.params) {
|
||||
let ty = self.insert_type_vars(ty);
|
||||
let ty = self.normalize_associated_types_in(ty);
|
||||
|
||||
|
|
|
@ -647,7 +647,7 @@ impl InferenceTable<'_> {
|
|||
let goal: InEnvironment<DomainGoal> =
|
||||
InEnvironment::new(&self.trait_env.env, coerce_unsized_tref.cast(Interner));
|
||||
|
||||
let canonicalized = self.canonicalize(goal);
|
||||
let canonicalized = self.canonicalize_with_free_vars(goal);
|
||||
|
||||
// FIXME: rustc's coerce_unsized is more specialized -- it only tries to
|
||||
// solve `CoerceUnsized` and `Unsize` goals at this point and leaves the
|
||||
|
|
|
@ -312,15 +312,13 @@ impl InferenceContext<'_> {
|
|||
Expr::Call { callee, args, .. } => {
|
||||
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
|
||||
let (res, derefed_callee) = 'b: {
|
||||
// manual loop to be able to access `derefs.table`
|
||||
while let Some((callee_deref_ty, _)) = derefs.next() {
|
||||
let res = derefs.table.callable_sig(&callee_deref_ty, args.len());
|
||||
if res.is_some() {
|
||||
break 'b (res, callee_deref_ty);
|
||||
}
|
||||
let (res, derefed_callee) = loop {
|
||||
let Some((callee_deref_ty, _)) = derefs.next() else {
|
||||
break (None, callee_ty.clone());
|
||||
};
|
||||
if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) {
|
||||
break (Some(res), callee_deref_ty);
|
||||
}
|
||||
(None, callee_ty.clone())
|
||||
};
|
||||
// if the function is unresolved, we use is_varargs=true to
|
||||
// suppress the arg count diagnostic here
|
||||
|
@ -657,7 +655,7 @@ impl InferenceContext<'_> {
|
|||
);
|
||||
}
|
||||
}
|
||||
if let Some(derefed) = builtin_deref(&mut self.table, &inner_ty, true) {
|
||||
if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) {
|
||||
self.resolve_ty_shallow(derefed)
|
||||
} else {
|
||||
deref_by_trait(&mut self.table, inner_ty)
|
||||
|
@ -774,7 +772,7 @@ impl InferenceContext<'_> {
|
|||
let receiver_adjustments = method_resolution::resolve_indexing_op(
|
||||
self.db,
|
||||
self.table.trait_env.clone(),
|
||||
canonicalized.value,
|
||||
canonicalized,
|
||||
index_trait,
|
||||
);
|
||||
let (self_ty, mut adj) = receiver_adjustments
|
||||
|
@ -1559,7 +1557,7 @@ impl InferenceContext<'_> {
|
|||
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
|
||||
let resolved = method_resolution::lookup_method(
|
||||
self.db,
|
||||
&canonicalized_receiver.value,
|
||||
&canonicalized_receiver,
|
||||
self.table.trait_env.clone(),
|
||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
||||
VisibleFromModule::Filter(self.resolver.module()),
|
||||
|
@ -1608,7 +1606,7 @@ impl InferenceContext<'_> {
|
|||
|
||||
let resolved = method_resolution::lookup_method(
|
||||
self.db,
|
||||
&canonicalized_receiver.value,
|
||||
&canonicalized_receiver,
|
||||
self.table.trait_env.clone(),
|
||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
||||
VisibleFromModule::Filter(self.resolver.module()),
|
||||
|
@ -1641,7 +1639,7 @@ impl InferenceContext<'_> {
|
|||
};
|
||||
|
||||
let assoc_func_with_same_name = method_resolution::iterate_method_candidates(
|
||||
&canonicalized_receiver.value,
|
||||
&canonicalized_receiver,
|
||||
self.db,
|
||||
self.table.trait_env.clone(),
|
||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
||||
|
|
|
@ -321,7 +321,7 @@ impl InferenceContext<'_> {
|
|||
|
||||
let mut not_visible = None;
|
||||
let res = method_resolution::iterate_method_candidates(
|
||||
&canonical_ty.value,
|
||||
&canonical_ty,
|
||||
self.db,
|
||||
self.table.trait_env.clone(),
|
||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
||||
|
|
|
@ -23,12 +23,9 @@ use crate::{
|
|||
};
|
||||
|
||||
impl InferenceContext<'_> {
|
||||
pub(super) fn canonicalize<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
|
||||
&mut self,
|
||||
t: T,
|
||||
) -> Canonicalized<T>
|
||||
pub(super) fn canonicalize<T>(&mut self, t: T) -> Canonical<T>
|
||||
where
|
||||
T: HasInterner<Interner = Interner>,
|
||||
T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
|
||||
{
|
||||
self.table.canonicalize(t)
|
||||
}
|
||||
|
@ -128,14 +125,14 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
|
|||
}),
|
||||
);
|
||||
for (i, v) in solution.value.iter(Interner).enumerate() {
|
||||
let var = self.free_vars[i].clone();
|
||||
let var = &self.free_vars[i];
|
||||
if let Some(ty) = v.ty(Interner) {
|
||||
// eagerly replace projections in the type; we may be getting types
|
||||
// e.g. from where clauses where this hasn't happened yet
|
||||
let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner));
|
||||
ctx.unify(var.assert_ty_ref(Interner), &ty);
|
||||
} else {
|
||||
let _ = ctx.try_unify(&var, &new_vars.apply(v.clone(), Interner));
|
||||
let _ = ctx.try_unify(var, &new_vars.apply(v.clone(), Interner));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -243,7 +240,7 @@ pub(crate) struct InferenceTable<'a> {
|
|||
pub(crate) db: &'a dyn HirDatabase,
|
||||
pub(crate) trait_env: Arc<TraitEnvironment>,
|
||||
var_unification_table: ChalkInferenceTable,
|
||||
type_variable_table: Vec<TypeVariableFlags>,
|
||||
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
|
||||
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
|
||||
/// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on
|
||||
/// temporary allocations.
|
||||
|
@ -252,8 +249,8 @@ pub(crate) struct InferenceTable<'a> {
|
|||
|
||||
pub(crate) struct InferenceTableSnapshot {
|
||||
var_table_snapshot: chalk_solve::infer::InferenceSnapshot<Interner>,
|
||||
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
|
||||
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
|
||||
type_variable_table_snapshot: Vec<TypeVariableFlags>,
|
||||
}
|
||||
|
||||
impl<'a> InferenceTable<'a> {
|
||||
|
@ -262,7 +259,7 @@ impl<'a> InferenceTable<'a> {
|
|||
db,
|
||||
trait_env,
|
||||
var_unification_table: ChalkInferenceTable::new(),
|
||||
type_variable_table: Vec::new(),
|
||||
type_variable_table: SmallVec::new(),
|
||||
pending_obligations: Vec::new(),
|
||||
resolve_obligations_buffer: Vec::new(),
|
||||
}
|
||||
|
@ -292,14 +289,14 @@ impl<'a> InferenceTable<'a> {
|
|||
}
|
||||
|
||||
fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty {
|
||||
let is_diverging = self
|
||||
.type_variable_table
|
||||
.get(iv.index() as usize)
|
||||
.map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING));
|
||||
if is_diverging {
|
||||
return TyKind::Never.intern(Interner);
|
||||
}
|
||||
match kind {
|
||||
_ if self
|
||||
.type_variable_table
|
||||
.get(iv.index() as usize)
|
||||
.map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)) =>
|
||||
{
|
||||
TyKind::Never
|
||||
}
|
||||
TyVariableKind::General => TyKind::Error,
|
||||
TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)),
|
||||
TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)),
|
||||
|
@ -307,12 +304,9 @@ impl<'a> InferenceTable<'a> {
|
|||
.intern(Interner)
|
||||
}
|
||||
|
||||
pub(crate) fn canonicalize<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
|
||||
&mut self,
|
||||
t: T,
|
||||
) -> Canonicalized<T>
|
||||
pub(crate) fn canonicalize_with_free_vars<T>(&mut self, t: T) -> Canonicalized<T>
|
||||
where
|
||||
T: HasInterner<Interner = Interner>,
|
||||
T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
|
||||
{
|
||||
// try to resolve obligations before canonicalizing, since this might
|
||||
// result in new knowledge about variables
|
||||
|
@ -326,6 +320,16 @@ impl<'a> InferenceTable<'a> {
|
|||
Canonicalized { value: result.quantified, free_vars }
|
||||
}
|
||||
|
||||
pub(crate) fn canonicalize<T>(&mut self, t: T) -> Canonical<T>
|
||||
where
|
||||
T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
|
||||
{
|
||||
// try to resolve obligations before canonicalizing, since this might
|
||||
// result in new knowledge about variables
|
||||
self.resolve_obligations_as_possible();
|
||||
self.var_unification_table.canonicalize(Interner, t).quantified
|
||||
}
|
||||
|
||||
/// Recurses through the given type, normalizing associated types mentioned
|
||||
/// in it by replacing them by type variables and registering obligations to
|
||||
/// resolve later. This should be done once for every type we get from some
|
||||
|
@ -541,7 +545,7 @@ impl<'a> InferenceTable<'a> {
|
|||
Err(_) => return false,
|
||||
};
|
||||
result.goals.iter().all(|goal| {
|
||||
let canonicalized = self.canonicalize(goal.clone());
|
||||
let canonicalized = self.canonicalize_with_free_vars(goal.clone());
|
||||
self.try_resolve_obligation(&canonicalized).is_some()
|
||||
})
|
||||
}
|
||||
|
@ -575,19 +579,15 @@ impl<'a> InferenceTable<'a> {
|
|||
|
||||
pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot {
|
||||
let var_table_snapshot = self.var_unification_table.snapshot();
|
||||
let type_variable_table_snapshot = self.type_variable_table.clone();
|
||||
let type_variable_table = self.type_variable_table.clone();
|
||||
let pending_obligations = self.pending_obligations.clone();
|
||||
InferenceTableSnapshot {
|
||||
var_table_snapshot,
|
||||
pending_obligations,
|
||||
type_variable_table_snapshot,
|
||||
}
|
||||
InferenceTableSnapshot { var_table_snapshot, pending_obligations, type_variable_table }
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) {
|
||||
self.var_unification_table.rollback_to(snapshot.var_table_snapshot);
|
||||
self.type_variable_table = snapshot.type_variable_table_snapshot;
|
||||
self.type_variable_table = snapshot.type_variable_table;
|
||||
self.pending_obligations = snapshot.pending_obligations;
|
||||
}
|
||||
|
||||
|
@ -606,7 +606,7 @@ impl<'a> InferenceTable<'a> {
|
|||
let in_env = InEnvironment::new(&self.trait_env.env, goal);
|
||||
let canonicalized = self.canonicalize(in_env);
|
||||
|
||||
self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized.value)
|
||||
self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized)
|
||||
}
|
||||
|
||||
pub(crate) fn register_obligation(&mut self, goal: Goal) {
|
||||
|
@ -615,7 +615,7 @@ impl<'a> InferenceTable<'a> {
|
|||
}
|
||||
|
||||
fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) {
|
||||
let canonicalized = self.canonicalize(goal);
|
||||
let canonicalized = self.canonicalize_with_free_vars(goal);
|
||||
let solution = self.try_resolve_obligation(&canonicalized);
|
||||
if matches!(solution, Some(Solution::Ambig(_))) {
|
||||
self.pending_obligations.push(canonicalized);
|
||||
|
@ -798,7 +798,7 @@ impl<'a> InferenceTable<'a> {
|
|||
let trait_data = self.db.trait_data(fn_once_trait);
|
||||
let output_assoc_type = trait_data.associated_type_by_name(&name![Output])?;
|
||||
|
||||
let mut arg_tys = vec![];
|
||||
let mut arg_tys = Vec::with_capacity(num_args);
|
||||
let arg_ty = TyBuilder::tuple(num_args)
|
||||
.fill(|it| {
|
||||
let arg = match it {
|
||||
|
@ -828,11 +828,7 @@ impl<'a> InferenceTable<'a> {
|
|||
environment: trait_env.clone(),
|
||||
};
|
||||
let canonical = self.canonicalize(obligation.clone());
|
||||
if self
|
||||
.db
|
||||
.trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner))
|
||||
.is_some()
|
||||
{
|
||||
if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some() {
|
||||
self.register_obligation(obligation.goal);
|
||||
let return_ty = self.normalize_projection_ty(projection);
|
||||
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
|
||||
|
@ -845,7 +841,7 @@ impl<'a> InferenceTable<'a> {
|
|||
let canonical = self.canonicalize(obligation.clone());
|
||||
if self
|
||||
.db
|
||||
.trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner))
|
||||
.trait_solve(krate, self.trait_env.block, canonical.cast(Interner))
|
||||
.is_some()
|
||||
{
|
||||
return Some((fn_x, arg_tys, return_ty));
|
||||
|
|
|
@ -371,8 +371,8 @@ pub fn layout_of_ty_query(
|
|||
TyKind::Never => cx.layout_of_never_type(),
|
||||
TyKind::Dyn(_) | TyKind::Foreign(_) => {
|
||||
let mut unit = layout_of_unit(&cx, dl)?;
|
||||
match unit.abi {
|
||||
Abi::Aggregate { ref mut sized } => *sized = false,
|
||||
match &mut unit.abi {
|
||||
Abi::Aggregate { sized } => *sized = false,
|
||||
_ => return Err(LayoutError::Unknown),
|
||||
}
|
||||
unit
|
||||
|
|
|
@ -213,7 +213,7 @@ impl TraitImpls {
|
|||
|
||||
// To better support custom derives, collect impls in all unnamed const items.
|
||||
// const _: () = { ... };
|
||||
for konst in module_data.scope.unnamed_consts(db.upcast()) {
|
||||
for konst in module_data.scope.unnamed_consts() {
|
||||
let body = db.body(konst.into());
|
||||
for (_, block_def_map) in body.blocks(db.upcast()) {
|
||||
Self::collect_def_map(db, map, &block_def_map);
|
||||
|
@ -337,7 +337,7 @@ impl InherentImpls {
|
|||
|
||||
// To better support custom derives, collect impls in all unnamed const items.
|
||||
// const _: () = { ... };
|
||||
for konst in module_data.scope.unnamed_consts(db.upcast()) {
|
||||
for konst in module_data.scope.unnamed_consts() {
|
||||
let body = db.body(konst.into());
|
||||
for (_, block_def_map) in body.blocks(db.upcast()) {
|
||||
self.collect_def_map(db, &block_def_map);
|
||||
|
@ -972,10 +972,9 @@ pub fn iterate_method_candidates_dyn(
|
|||
|
||||
deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| {
|
||||
iterate_method_candidates_with_autoref(
|
||||
&receiver_ty,
|
||||
&mut table,
|
||||
receiver_ty,
|
||||
adj,
|
||||
db,
|
||||
env.clone(),
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
|
@ -1000,10 +999,9 @@ pub fn iterate_method_candidates_dyn(
|
|||
|
||||
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||
fn iterate_method_candidates_with_autoref(
|
||||
receiver_ty: &Canonical<Ty>,
|
||||
table: &mut InferenceTable<'_>,
|
||||
receiver_ty: Canonical<Ty>,
|
||||
first_adjustment: ReceiverAdjustments,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
|
@ -1016,10 +1014,9 @@ fn iterate_method_candidates_with_autoref(
|
|||
|
||||
let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| {
|
||||
iterate_method_candidates_by_receiver(
|
||||
table,
|
||||
receiver_ty,
|
||||
first_adjustment,
|
||||
db,
|
||||
env.clone(),
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
|
@ -1034,7 +1031,7 @@ fn iterate_method_candidates_with_autoref(
|
|||
maybe_reborrowed.autoderefs += 1;
|
||||
}
|
||||
|
||||
iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?;
|
||||
iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?;
|
||||
|
||||
let refed = Canonical {
|
||||
value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())
|
||||
|
@ -1042,7 +1039,7 @@ fn iterate_method_candidates_with_autoref(
|
|||
binders: receiver_ty.binders.clone(),
|
||||
};
|
||||
|
||||
iterate_method_candidates_by_receiver(&refed, first_adjustment.with_autoref(Mutability::Not))?;
|
||||
iterate_method_candidates_by_receiver(refed, first_adjustment.with_autoref(Mutability::Not))?;
|
||||
|
||||
let ref_muted = Canonical {
|
||||
value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone())
|
||||
|
@ -1050,58 +1047,53 @@ fn iterate_method_candidates_with_autoref(
|
|||
binders: receiver_ty.binders.clone(),
|
||||
};
|
||||
|
||||
iterate_method_candidates_by_receiver(
|
||||
&ref_muted,
|
||||
first_adjustment.with_autoref(Mutability::Mut),
|
||||
)
|
||||
iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||
fn iterate_method_candidates_by_receiver(
|
||||
receiver_ty: &Canonical<Ty>,
|
||||
table: &mut InferenceTable<'_>,
|
||||
receiver_ty: Canonical<Ty>,
|
||||
receiver_adjustments: ReceiverAdjustments,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let receiver_ty = table.instantiate_canonical(receiver_ty.clone());
|
||||
let snapshot = table.snapshot();
|
||||
// We're looking for methods with *receiver* type receiver_ty. These could
|
||||
// be found in any of the derefs of receiver_ty, so we have to go through
|
||||
// that, including raw derefs.
|
||||
let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true);
|
||||
while let Some((self_ty, _)) = autoderef.next() {
|
||||
iterate_inherent_methods(
|
||||
&self_ty,
|
||||
autoderef.table,
|
||||
name,
|
||||
Some(&receiver_ty),
|
||||
Some(receiver_adjustments.clone()),
|
||||
visible_from_module,
|
||||
&mut callback,
|
||||
)?
|
||||
}
|
||||
|
||||
table.rollback_to(snapshot);
|
||||
|
||||
let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true);
|
||||
while let Some((self_ty, _)) = autoderef.next() {
|
||||
iterate_trait_method_candidates(
|
||||
&self_ty,
|
||||
autoderef.table,
|
||||
traits_in_scope,
|
||||
name,
|
||||
Some(&receiver_ty),
|
||||
Some(receiver_adjustments.clone()),
|
||||
&mut callback,
|
||||
)?
|
||||
}
|
||||
|
||||
ControlFlow::Continue(())
|
||||
table.run_in_snapshot(|table| {
|
||||
let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true);
|
||||
while let Some((self_ty, _)) = autoderef.next() {
|
||||
iterate_inherent_methods(
|
||||
&self_ty,
|
||||
autoderef.table,
|
||||
name,
|
||||
Some(&receiver_ty),
|
||||
Some(receiver_adjustments.clone()),
|
||||
visible_from_module,
|
||||
&mut callback,
|
||||
)?
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
})?;
|
||||
table.run_in_snapshot(|table| {
|
||||
let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true);
|
||||
while let Some((self_ty, _)) = autoderef.next() {
|
||||
iterate_trait_method_candidates(
|
||||
&self_ty,
|
||||
autoderef.table,
|
||||
traits_in_scope,
|
||||
name,
|
||||
Some(&receiver_ty),
|
||||
Some(receiver_adjustments.clone()),
|
||||
&mut callback,
|
||||
)?
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||
|
@ -1147,9 +1139,9 @@ fn iterate_trait_method_candidates(
|
|||
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
let db = table.db;
|
||||
let env = table.trait_env.clone();
|
||||
|
||||
let canonical_self_ty = table.canonicalize(self_ty.clone()).value;
|
||||
let canonical_self_ty = table.canonicalize(self_ty.clone());
|
||||
let TraitEnvironment { krate, block, .. } = *table.trait_env;
|
||||
|
||||
'traits: for &t in traits_in_scope {
|
||||
let data = db.trait_data(t);
|
||||
|
@ -1164,7 +1156,7 @@ fn iterate_trait_method_candidates(
|
|||
{
|
||||
// FIXME: this should really be using the edition of the method name's span, in case it
|
||||
// comes from a macro
|
||||
if db.crate_graph()[env.krate].edition < Edition::Edition2021 {
|
||||
if db.crate_graph()[krate].edition < Edition::Edition2021 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -1183,8 +1175,8 @@ fn iterate_trait_method_candidates(
|
|||
IsValidCandidate::No => continue,
|
||||
};
|
||||
if !known_implemented {
|
||||
let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty);
|
||||
if db.trait_solve(env.krate, env.block, goal.cast(Interner)).is_none() {
|
||||
let goal = generic_implements_goal(db, &table.trait_env, t, &canonical_self_ty);
|
||||
if db.trait_solve(krate, block, goal.cast(Interner)).is_none() {
|
||||
continue 'traits;
|
||||
}
|
||||
}
|
||||
|
@ -1365,7 +1357,7 @@ pub(crate) fn resolve_indexing_op(
|
|||
let ty = table.instantiate_canonical(ty);
|
||||
let deref_chain = autoderef_method_receiver(&mut table, ty);
|
||||
for (ty, adj) in deref_chain {
|
||||
let goal = generic_implements_goal(db, table.trait_env.clone(), index_trait, &ty);
|
||||
let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty);
|
||||
if db
|
||||
.trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner))
|
||||
.is_some()
|
||||
|
@ -1548,7 +1540,7 @@ fn is_valid_impl_fn_candidate(
|
|||
|
||||
for goal in goals.clone() {
|
||||
let in_env = InEnvironment::new(&table.trait_env.env, goal);
|
||||
let canonicalized = table.canonicalize(in_env);
|
||||
let canonicalized = table.canonicalize_with_free_vars(in_env);
|
||||
let solution = table.db.trait_solve(
|
||||
table.trait_env.krate,
|
||||
table.trait_env.block,
|
||||
|
@ -1586,10 +1578,10 @@ fn is_valid_impl_fn_candidate(
|
|||
pub fn implements_trait(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
env: &TraitEnvironment,
|
||||
trait_: TraitId,
|
||||
) -> bool {
|
||||
let goal = generic_implements_goal(db, env.clone(), trait_, ty);
|
||||
let goal = generic_implements_goal(db, env, trait_, ty);
|
||||
let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner));
|
||||
|
||||
solution.is_some()
|
||||
|
@ -1598,10 +1590,10 @@ pub fn implements_trait(
|
|||
pub fn implements_trait_unique(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
env: &TraitEnvironment,
|
||||
trait_: TraitId,
|
||||
) -> bool {
|
||||
let goal = generic_implements_goal(db, env.clone(), trait_, ty);
|
||||
let goal = generic_implements_goal(db, env, trait_, ty);
|
||||
let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner));
|
||||
|
||||
matches!(solution, Some(crate::Solution::Unique(_)))
|
||||
|
@ -1612,32 +1604,34 @@ pub fn implements_trait_unique(
|
|||
#[tracing::instrument(skip_all)]
|
||||
fn generic_implements_goal(
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
env: &TraitEnvironment,
|
||||
trait_: TraitId,
|
||||
self_ty: &Canonical<Ty>,
|
||||
) -> Canonical<InEnvironment<super::DomainGoal>> {
|
||||
let mut kinds = self_ty.binders.interned().to_vec();
|
||||
let binders = self_ty.binders.interned();
|
||||
let trait_ref = TyBuilder::trait_ref(db, trait_)
|
||||
.push(self_ty.value.clone())
|
||||
.fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
|
||||
.fill_with_bound_vars(DebruijnIndex::INNERMOST, binders.len())
|
||||
.build();
|
||||
kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|it| {
|
||||
let vk = match it.data(Interner) {
|
||||
chalk_ir::GenericArgData::Ty(_) => {
|
||||
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
||||
}
|
||||
chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
|
||||
chalk_ir::GenericArgData::Const(c) => {
|
||||
chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
|
||||
}
|
||||
};
|
||||
chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
|
||||
}));
|
||||
|
||||
let kinds =
|
||||
binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| {
|
||||
let vk = match it.data(Interner) {
|
||||
chalk_ir::GenericArgData::Ty(_) => {
|
||||
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
||||
}
|
||||
chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
|
||||
chalk_ir::GenericArgData::Const(c) => {
|
||||
chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
|
||||
}
|
||||
};
|
||||
chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
|
||||
}));
|
||||
let binders = CanonicalVarKinds::from_iter(Interner, kinds);
|
||||
|
||||
let obligation = trait_ref.cast(Interner);
|
||||
Canonical {
|
||||
binders: CanonicalVarKinds::from_iter(Interner, kinds),
|
||||
value: InEnvironment::new(&env.env, obligation),
|
||||
}
|
||||
let value = InEnvironment::new(&env.env, obligation);
|
||||
Canonical { binders, value }
|
||||
}
|
||||
|
||||
fn autoderef_method_receiver(
|
||||
|
@ -1648,7 +1642,7 @@ fn autoderef_method_receiver(
|
|||
let mut autoderef = autoderef::Autoderef::new(table, ty, false);
|
||||
while let Some((ty, derefs)) = autoderef.next() {
|
||||
deref_chain.push((
|
||||
autoderef.table.canonicalize(ty).value,
|
||||
autoderef.table.canonicalize(ty),
|
||||
ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false },
|
||||
));
|
||||
}
|
||||
|
|
|
@ -1165,6 +1165,7 @@ impl MirBody {
|
|||
pub enum MirSpan {
|
||||
ExprId(ExprId),
|
||||
PatId(PatId),
|
||||
SelfParam,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
|
|
|
@ -376,6 +376,10 @@ impl MirEvalError {
|
|||
Ok(s) => s.map(|it| it.syntax_node_ptr()),
|
||||
Err(_) => continue,
|
||||
},
|
||||
MirSpan::SelfParam => match source_map.self_param_syntax() {
|
||||
Some(s) => s.map(|it| it.syntax_node_ptr()),
|
||||
None => continue,
|
||||
},
|
||||
MirSpan::Unknown => continue,
|
||||
};
|
||||
let file_id = span.file_id.original_file(db.upcast());
|
||||
|
|
|
@ -1810,9 +1810,20 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
fn lower_params_and_bindings(
|
||||
&mut self,
|
||||
params: impl Iterator<Item = (PatId, Ty)> + Clone,
|
||||
self_binding: Option<(BindingId, Ty)>,
|
||||
pick_binding: impl Fn(BindingId) -> bool,
|
||||
) -> Result<BasicBlockId> {
|
||||
let base_param_count = self.result.param_locals.len();
|
||||
let self_binding = match self_binding {
|
||||
Some((self_binding, ty)) => {
|
||||
let local_id = self.result.locals.alloc(Local { ty });
|
||||
self.drop_scopes.last_mut().unwrap().locals.push(local_id);
|
||||
self.result.binding_locals.insert(self_binding, local_id);
|
||||
self.result.param_locals.push(local_id);
|
||||
Some(self_binding)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
self.result.param_locals.extend(params.clone().map(|(it, ty)| {
|
||||
let local_id = self.result.locals.alloc(Local { ty });
|
||||
self.drop_scopes.last_mut().unwrap().locals.push(local_id);
|
||||
|
@ -1838,9 +1849,23 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
}
|
||||
let mut current = self.result.start_block;
|
||||
for ((param, _), local) in
|
||||
params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count))
|
||||
{
|
||||
if let Some(self_binding) = self_binding {
|
||||
let local = self.result.param_locals.clone()[base_param_count];
|
||||
if local != self.binding_local(self_binding)? {
|
||||
let r = self.match_self_param(self_binding, current, local)?;
|
||||
if let Some(b) = r.1 {
|
||||
self.set_terminator(b, TerminatorKind::Unreachable, MirSpan::SelfParam);
|
||||
}
|
||||
current = r.0;
|
||||
}
|
||||
}
|
||||
let local_params = self
|
||||
.result
|
||||
.param_locals
|
||||
.clone()
|
||||
.into_iter()
|
||||
.skip(base_param_count + self_binding.is_some() as usize);
|
||||
for ((param, _), local) in params.zip(local_params) {
|
||||
if let Pat::Bind { id, .. } = self.body[param] {
|
||||
if local == self.binding_local(id)? {
|
||||
continue;
|
||||
|
@ -2019,6 +2044,7 @@ pub fn mir_body_for_closure_query(
|
|||
};
|
||||
let current = ctx.lower_params_and_bindings(
|
||||
args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())),
|
||||
None,
|
||||
|_| true,
|
||||
)?;
|
||||
if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
|
||||
|
@ -2149,16 +2175,16 @@ pub fn lower_to_mir(
|
|||
let substs = TyBuilder::placeholder_subst(db, fid);
|
||||
let callable_sig =
|
||||
db.callable_item_signature(fid.into()).substitute(Interner, &substs);
|
||||
let mut params = callable_sig.params().iter();
|
||||
let self_param = body.self_param.and_then(|id| Some((id, params.next()?.clone())));
|
||||
break 'b ctx.lower_params_and_bindings(
|
||||
body.params
|
||||
.iter()
|
||||
.zip(callable_sig.params().iter())
|
||||
.map(|(it, y)| (*it, y.clone())),
|
||||
body.params.iter().zip(params).map(|(it, y)| (*it, y.clone())),
|
||||
self_param,
|
||||
binding_picker,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
|
||||
ctx.lower_params_and_bindings([].into_iter(), None, binding_picker)?
|
||||
};
|
||||
if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
|
||||
let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?;
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
Substitution, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, TyBuilder, TyKind,
|
||||
ValueNs, VariantData, VariantId,
|
||||
},
|
||||
MutBorrowKind,
|
||||
LocalId, MutBorrowKind,
|
||||
},
|
||||
BindingMode,
|
||||
};
|
||||
|
@ -82,6 +82,22 @@ impl MirLowerCtx<'_> {
|
|||
Ok((current, current_else))
|
||||
}
|
||||
|
||||
pub(super) fn match_self_param(
|
||||
&mut self,
|
||||
id: BindingId,
|
||||
current: BasicBlockId,
|
||||
local: LocalId,
|
||||
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||
self.pattern_match_binding(
|
||||
id,
|
||||
BindingMode::Move,
|
||||
local.into(),
|
||||
MirSpan::SelfParam,
|
||||
current,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn pattern_match_inner(
|
||||
&mut self,
|
||||
mut current: BasicBlockId,
|
||||
|
@ -283,9 +299,9 @@ impl MirLowerCtx<'_> {
|
|||
(current, current_else) =
|
||||
self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
|
||||
}
|
||||
if let Some(slice) = slice {
|
||||
if let &Some(slice) = slice {
|
||||
if mode == MatchingMode::Bind {
|
||||
if let Pat::Bind { id, subpat: _ } = self.body[*slice] {
|
||||
if let Pat::Bind { id, subpat: _ } = self.body[slice] {
|
||||
let next_place = cond_place.project(
|
||||
ProjectionElem::Subslice {
|
||||
from: prefix.len() as u64,
|
||||
|
@ -293,11 +309,12 @@ impl MirLowerCtx<'_> {
|
|||
},
|
||||
&mut self.result.projection_store,
|
||||
);
|
||||
let mode = self.infer.binding_modes[slice];
|
||||
(current, current_else) = self.pattern_match_binding(
|
||||
id,
|
||||
*slice,
|
||||
mode,
|
||||
next_place,
|
||||
(*slice).into(),
|
||||
(slice).into(),
|
||||
current,
|
||||
current_else,
|
||||
)?;
|
||||
|
@ -398,9 +415,10 @@ impl MirLowerCtx<'_> {
|
|||
self.pattern_match_inner(current, current_else, cond_place, *subpat, mode)?
|
||||
}
|
||||
if mode == MatchingMode::Bind {
|
||||
let mode = self.infer.binding_modes[pattern];
|
||||
self.pattern_match_binding(
|
||||
*id,
|
||||
pattern,
|
||||
mode,
|
||||
cond_place,
|
||||
pattern.into(),
|
||||
current,
|
||||
|
@ -437,14 +455,13 @@ impl MirLowerCtx<'_> {
|
|||
fn pattern_match_binding(
|
||||
&mut self,
|
||||
id: BindingId,
|
||||
pat: PatId,
|
||||
mode: BindingMode,
|
||||
cond_place: Place,
|
||||
span: MirSpan,
|
||||
current: BasicBlockId,
|
||||
current_else: Option<BasicBlockId>,
|
||||
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||
let target_place = self.binding_local(id)?;
|
||||
let mode = self.infer.binding_modes[pat];
|
||||
self.push_storage_live(id, current)?;
|
||||
self.push_assignment(
|
||||
current,
|
||||
|
|
|
@ -12,7 +12,7 @@ mod traits;
|
|||
|
||||
use std::env;
|
||||
|
||||
use base_db::{FileRange, SourceDatabaseExt};
|
||||
use base_db::{FileRange, SourceDatabaseExt2 as _};
|
||||
use expect_test::Expect;
|
||||
use hir_def::{
|
||||
body::{Body, BodySourceMap, SyntheticSyntax},
|
||||
|
@ -164,7 +164,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
|||
Some(value) => value,
|
||||
None => continue,
|
||||
};
|
||||
let range = node.as_ref().original_file_range(&db);
|
||||
let range = node.as_ref().original_file_range_rooted(&db);
|
||||
if let Some(expected) = types.remove(&range) {
|
||||
let actual = if display_source {
|
||||
ty.display_source_code(&db, def.module(&db), true).unwrap()
|
||||
|
@ -180,7 +180,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
|||
Some(value) => value,
|
||||
None => continue,
|
||||
};
|
||||
let range = node.as_ref().original_file_range(&db);
|
||||
let range = node.as_ref().original_file_range_rooted(&db);
|
||||
if let Some(expected) = types.remove(&range) {
|
||||
let actual = if display_source {
|
||||
ty.display_source_code(&db, def.module(&db), true).unwrap()
|
||||
|
@ -211,7 +211,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
|||
}) else {
|
||||
continue;
|
||||
};
|
||||
let range = node.as_ref().original_file_range(&db);
|
||||
let range = node.as_ref().original_file_range_rooted(&db);
|
||||
let actual = format!(
|
||||
"expected {}, got {}",
|
||||
mismatch.expected.display_test(&db),
|
||||
|
@ -293,20 +293,29 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
|||
let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
|
||||
let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
|
||||
|
||||
if let Some(self_param) = body.self_param {
|
||||
let ty = &inference_result.type_of_binding[self_param];
|
||||
if let Some(syntax_ptr) = body_source_map.self_param_syntax() {
|
||||
let root = db.parse_or_expand(syntax_ptr.file_id);
|
||||
let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone());
|
||||
types.push((node.clone(), ty));
|
||||
}
|
||||
}
|
||||
|
||||
for (pat, mut ty) in inference_result.type_of_pat.iter() {
|
||||
if let Pat::Bind { id, .. } = body.pats[pat] {
|
||||
ty = &inference_result.type_of_binding[id];
|
||||
}
|
||||
let syntax_ptr = match body_source_map.pat_syntax(pat) {
|
||||
let node = match body_source_map.pat_syntax(pat) {
|
||||
Ok(sp) => {
|
||||
let root = db.parse_or_expand(sp.file_id);
|
||||
sp.map(|ptr| ptr.to_node(&root).syntax().clone())
|
||||
}
|
||||
Err(SyntheticSyntax) => continue,
|
||||
};
|
||||
types.push((syntax_ptr.clone(), ty));
|
||||
types.push((node.clone(), ty));
|
||||
if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) {
|
||||
mismatches.push((syntax_ptr, mismatch));
|
||||
mismatches.push((node, mismatch));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -575,7 +584,7 @@ fn salsa_bug() {
|
|||
}
|
||||
";
|
||||
|
||||
db.set_file_text(pos.file_id, Arc::from(new_text));
|
||||
db.set_file_text(pos.file_id, new_text);
|
||||
|
||||
let module = db.module_for_file(pos.file_id);
|
||||
let crate_def_map = module.def_map(&db);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use base_db::SourceDatabaseExt;
|
||||
use base_db::SourceDatabaseExt2 as _;
|
||||
use test_fixture::WithFixture;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{db::HirDatabase, test_db::TestDB};
|
||||
|
||||
|
@ -33,7 +32,7 @@ fn foo() -> i32 {
|
|||
1
|
||||
}";
|
||||
|
||||
db.set_file_text(pos.file_id, Arc::from(new_text));
|
||||
db.set_file_text(pos.file_id, new_text);
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
@ -85,7 +84,7 @@ fn baz() -> i32 {
|
|||
}
|
||||
";
|
||||
|
||||
db.set_file_text(pos.file_id, Arc::from(new_text));
|
||||
db.set_file_text(pos.file_id, new_text);
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
|
|
@ -1461,28 +1461,6 @@ fn f() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_impl_in_synstructure_const() {
|
||||
check_types(
|
||||
r#"
|
||||
struct S;
|
||||
|
||||
trait Tr {
|
||||
fn method(&self) -> u16;
|
||||
}
|
||||
|
||||
const _DERIVE_Tr_: () = {
|
||||
impl Tr for S {}
|
||||
};
|
||||
|
||||
fn f() {
|
||||
S.method();
|
||||
//^^^^^^^^^^ u16
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inherent_impl_in_unnamed_const() {
|
||||
check_types(
|
||||
|
@ -1795,6 +1773,21 @@ fn test() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deref_into_inference_var() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore:deref
|
||||
struct A<T>(T);
|
||||
impl core::ops::Deref for A<u32> {}
|
||||
impl A<i32> { fn foo(&self) {} }
|
||||
fn main() {
|
||||
A(0).foo();
|
||||
//^^^^^^^^^^ ()
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn receiver_adjustment_autoref() {
|
||||
check(
|
||||
|
|
|
@ -2121,6 +2121,7 @@ async fn main() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
16..193 '{ ...2 }; }': ()
|
||||
16..193 '{ ...2 }; }': impl Future<Output = ()>
|
||||
26..27 'x': i32
|
||||
30..43 'unsafe { 92 }': i32
|
||||
39..41 '92': i32
|
||||
|
@ -2131,6 +2132,8 @@ async fn main() {
|
|||
73..75 '()': ()
|
||||
95..96 'z': ControlFlow<(), ()>
|
||||
130..140 'try { () }': ControlFlow<(), ()>
|
||||
130..140 'try { () }': fn from_output<ControlFlow<(), ()>>(<ControlFlow<(), ()> as Try>::Output) -> ControlFlow<(), ()>
|
||||
130..140 'try { () }': ControlFlow<(), ()>
|
||||
136..138 '()': ()
|
||||
150..151 'w': i32
|
||||
154..166 'const { 92 }': i32
|
||||
|
|
|
@ -204,7 +204,7 @@ pub struct NoSuchField {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct PrivateAssocItem {
|
||||
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, Either<ast::Pat, ast::SelfParam>>>>,
|
||||
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
|
||||
pub item: AssocItem,
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ pub struct UnresolvedMethodCall {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedAssocItem {
|
||||
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, Either<ast::Pat, ast::SelfParam>>>>,
|
||||
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -159,6 +159,7 @@ impl HirDisplay for Adt {
|
|||
impl HirDisplay for Struct {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
let module_id = self.module(f.db).id;
|
||||
// FIXME: Render repr if its set explicitly?
|
||||
write_visibility(module_id, self.visibility(f.db), f)?;
|
||||
f.write_str("struct ")?;
|
||||
write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
|
||||
|
@ -166,37 +167,40 @@ impl HirDisplay for Struct {
|
|||
write_generic_params(def_id, f)?;
|
||||
|
||||
let variant_data = self.variant_data(f.db);
|
||||
if let StructKind::Tuple = variant_data.kind() {
|
||||
f.write_char('(')?;
|
||||
let mut it = variant_data.fields().iter().peekable();
|
||||
match variant_data.kind() {
|
||||
StructKind::Tuple => {
|
||||
f.write_char('(')?;
|
||||
let mut it = variant_data.fields().iter().peekable();
|
||||
|
||||
while let Some((id, _)) = it.next() {
|
||||
let field = Field { parent: (*self).into(), id };
|
||||
write_visibility(module_id, field.visibility(f.db), f)?;
|
||||
field.ty(f.db).hir_fmt(f)?;
|
||||
if it.peek().is_some() {
|
||||
f.write_str(", ")?;
|
||||
while let Some((id, _)) = it.next() {
|
||||
let field = Field { parent: (*self).into(), id };
|
||||
write_visibility(module_id, field.visibility(f.db), f)?;
|
||||
field.ty(f.db).hir_fmt(f)?;
|
||||
if it.peek().is_some() {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
f.write_char(')')?;
|
||||
write_where_clause(def_id, f)?;
|
||||
}
|
||||
StructKind::Record => {
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
let fields = self.fields(f.db);
|
||||
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
|
||||
if fields.is_empty() {
|
||||
f.write_str("{}")?;
|
||||
} else {
|
||||
f.write_str("{\n")?;
|
||||
for field in self.fields(f.db) {
|
||||
f.write_str(" ")?;
|
||||
field.hir_fmt(f)?;
|
||||
f.write_str(",\n")?;
|
||||
}
|
||||
f.write_str("}")?;
|
||||
}
|
||||
}
|
||||
|
||||
f.write_str(");")?;
|
||||
}
|
||||
|
||||
write_where_clause(def_id, f)?;
|
||||
|
||||
if let StructKind::Record = variant_data.kind() {
|
||||
let fields = self.fields(f.db);
|
||||
if fields.is_empty() {
|
||||
f.write_str(" {}")?;
|
||||
} else {
|
||||
f.write_str(" {\n")?;
|
||||
for field in self.fields(f.db) {
|
||||
f.write_str(" ")?;
|
||||
field.hir_fmt(f)?;
|
||||
f.write_str(",\n")?;
|
||||
}
|
||||
f.write_str("}")?;
|
||||
}
|
||||
StructKind::Unit => _ = write_where_clause(def_id, f)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -210,11 +214,12 @@ impl HirDisplay for Enum {
|
|||
write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
|
||||
let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id));
|
||||
write_generic_params(def_id, f)?;
|
||||
write_where_clause(def_id, f)?;
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
|
||||
let variants = self.variants(f.db);
|
||||
if !variants.is_empty() {
|
||||
f.write_str(" {\n")?;
|
||||
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
|
||||
f.write_str("{\n")?;
|
||||
for variant in variants {
|
||||
f.write_str(" ")?;
|
||||
variant.hir_fmt(f)?;
|
||||
|
@ -234,11 +239,12 @@ impl HirDisplay for Union {
|
|||
write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
|
||||
let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id));
|
||||
write_generic_params(def_id, f)?;
|
||||
write_where_clause(def_id, f)?;
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
|
||||
let fields = self.fields(f.db);
|
||||
if !fields.is_empty() {
|
||||
f.write_str(" {\n")?;
|
||||
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
|
||||
f.write_str("{\n")?;
|
||||
for field in self.fields(f.db) {
|
||||
f.write_str(" ")?;
|
||||
field.hir_fmt(f)?;
|
||||
|
@ -446,7 +452,10 @@ fn write_generic_params(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
fn write_where_clause(
|
||||
def: GenericDefId,
|
||||
f: &mut HirFormatter<'_>,
|
||||
) -> Result<bool, HirDisplayError> {
|
||||
let params = f.db.generic_params(def);
|
||||
|
||||
// unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`.
|
||||
|
@ -465,7 +474,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
|
|||
});
|
||||
|
||||
if !has_displayable_predicate {
|
||||
return Ok(());
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter<'_>| match target {
|
||||
|
@ -543,7 +552,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
|
|||
// End of final predicate. There must be at least one predicate here.
|
||||
f.write_char(',')?;
|
||||
|
||||
Ok(())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
impl HirDisplay for Const {
|
||||
|
@ -594,19 +603,20 @@ impl HirDisplay for Trait {
|
|||
write!(f, "trait {}", data.name.display(f.db.upcast()))?;
|
||||
let def_id = GenericDefId::TraitId(self.id);
|
||||
write_generic_params(def_id, f)?;
|
||||
write_where_clause(def_id, f)?;
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
|
||||
if let Some(limit) = f.entity_limit {
|
||||
let assoc_items = self.items(f.db);
|
||||
let count = assoc_items.len().min(limit);
|
||||
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
|
||||
if count == 0 {
|
||||
if assoc_items.is_empty() {
|
||||
f.write_str(" {}")?;
|
||||
f.write_str("{}")?;
|
||||
} else {
|
||||
f.write_str(" { /* … */ }")?;
|
||||
f.write_str("{ /* … */ }")?;
|
||||
}
|
||||
} else {
|
||||
f.write_str(" {\n")?;
|
||||
f.write_str("{\n")?;
|
||||
for item in &assoc_items[..count] {
|
||||
f.write_str(" ")?;
|
||||
match item {
|
||||
|
@ -651,7 +661,6 @@ impl HirDisplay for TypeAlias {
|
|||
write!(f, "type {}", data.name.display(f.db.upcast()))?;
|
||||
let def_id = GenericDefId::TypeAliasId(self.id);
|
||||
write_generic_params(def_id, f)?;
|
||||
write_where_clause(def_id, f)?;
|
||||
if !data.bounds.is_empty() {
|
||||
f.write_str(": ")?;
|
||||
f.write_joined(data.bounds.iter(), " + ")?;
|
||||
|
@ -660,6 +669,7 @@ impl HirDisplay for TypeAlias {
|
|||
f.write_str(" = ")?;
|
||||
ty.hir_fmt(f)?;
|
||||
}
|
||||
write_where_clause(def_id, f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use hir_def::{
|
|||
};
|
||||
use hir_expand::{HirFileId, InFile};
|
||||
use syntax::ast;
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, Adt, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
|
||||
|
@ -37,6 +38,12 @@ impl Module {
|
|||
def_map[self.id.local_id].definition_source(db.upcast())
|
||||
}
|
||||
|
||||
/// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
|
||||
pub fn definition_source_range(self, db: &dyn HirDatabase) -> InFile<TextRange> {
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map[self.id.local_id].definition_source_range(db.upcast())
|
||||
}
|
||||
|
||||
pub fn definition_source_file_id(self, db: &dyn HirDatabase) -> HirFileId {
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map[self.id.local_id].definition_source_file_id()
|
||||
|
@ -71,6 +78,13 @@ impl Module {
|
|||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map[self.id.local_id].declaration_source(db.upcast())
|
||||
}
|
||||
|
||||
/// Returns a text range which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root.
|
||||
pub fn declaration_source_range(self, db: &dyn HirDatabase) -> Option<InFile<TextRange>> {
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map[self.id.local_id].declaration_source_range(db.upcast())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for Field {
|
||||
|
|
|
@ -56,8 +56,8 @@ use hir_def::{
|
|||
AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId,
|
||||
EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule,
|
||||
ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander,
|
||||
MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId,
|
||||
TypeOrConstParamId, TypeParamId, UnionId,
|
||||
ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, TypeOrConstParamId,
|
||||
TypeParamId, UnionId,
|
||||
};
|
||||
use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind};
|
||||
use hir_ty::{
|
||||
|
@ -122,7 +122,7 @@ pub use {
|
|||
visibility::Visibility,
|
||||
// FIXME: This is here since some queries take it as input that are used
|
||||
// outside of hir.
|
||||
{AdtId, ModuleDefId},
|
||||
{AdtId, MacroId, ModuleDefId},
|
||||
},
|
||||
hir_expand::{
|
||||
attrs::{Attr, AttrId},
|
||||
|
@ -754,7 +754,7 @@ impl Module {
|
|||
scope
|
||||
.declarations()
|
||||
.map(ModuleDef::from)
|
||||
.chain(scope.unnamed_consts(db.upcast()).map(|id| ModuleDef::Const(Const::from(id))))
|
||||
.chain(scope.unnamed_consts().map(|id| ModuleDef::Const(Const::from(id))))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
@ -1725,6 +1725,10 @@ impl DefWithBody {
|
|||
Ok(s) => s.map(|it| it.into()),
|
||||
Err(_) => continue,
|
||||
},
|
||||
mir::MirSpan::SelfParam => match source_map.self_param_syntax() {
|
||||
Some(s) => s.map(|it| it.into()),
|
||||
None => continue,
|
||||
},
|
||||
mir::MirSpan::Unknown => continue,
|
||||
};
|
||||
acc.push(
|
||||
|
@ -1776,6 +1780,11 @@ impl DefWithBody {
|
|||
Ok(s) => s.map(|it| it.into()),
|
||||
Err(_) => continue,
|
||||
},
|
||||
mir::MirSpan::SelfParam => match source_map.self_param_syntax()
|
||||
{
|
||||
Some(s) => s.map(|it| it.into()),
|
||||
None => continue,
|
||||
},
|
||||
mir::MirSpan::Unknown => continue,
|
||||
};
|
||||
acc.push(NeedMut { local, span }.into());
|
||||
|
@ -2127,8 +2136,11 @@ impl Param {
|
|||
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
|
||||
let parent = DefWithBodyId::FunctionId(self.func.into());
|
||||
let body = db.body(parent);
|
||||
let pat_id = body.params[self.idx];
|
||||
if let Pat::Bind { id, .. } = &body[pat_id] {
|
||||
if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
|
||||
Some(Local { parent, binding_id: self_param })
|
||||
} else if let Pat::Bind { id, .. } =
|
||||
&body[body.params[self.idx - body.self_param.is_some() as usize]]
|
||||
{
|
||||
Some(Local { parent, binding_id: *id })
|
||||
} else {
|
||||
None
|
||||
|
@ -2143,7 +2155,7 @@ impl Param {
|
|||
let InFile { file_id, value } = self.func.source(db)?;
|
||||
let params = value.param_list()?;
|
||||
if params.self_param().is_some() {
|
||||
params.params().nth(self.idx.checked_sub(1)?)
|
||||
params.params().nth(self.idx.checked_sub(params.self_param().is_some() as usize)?)
|
||||
} else {
|
||||
params.params().nth(self.idx)
|
||||
}
|
||||
|
@ -2605,6 +2617,15 @@ impl Macro {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_env_or_option_env(&self, db: &dyn HirDatabase) -> bool {
|
||||
match self.id {
|
||||
MacroId::Macro2Id(it) => {
|
||||
matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env())
|
||||
}
|
||||
MacroId::MacroRulesId(_) | MacroId::ProcMacroId(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_attr(&self, db: &dyn HirDatabase) -> bool {
|
||||
matches!(self.kind(db), MacroKind::Attr)
|
||||
}
|
||||
|
@ -3134,35 +3155,59 @@ impl Local {
|
|||
/// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;`
|
||||
pub fn sources(self, db: &dyn HirDatabase) -> Vec<LocalSource> {
|
||||
let (body, source_map) = db.body_with_source_map(self.parent);
|
||||
self.sources_(db, &body, &source_map).collect()
|
||||
match body.self_param.zip(source_map.self_param_syntax()) {
|
||||
Some((param, source)) if param == self.binding_id => {
|
||||
let root = source.file_syntax(db.upcast());
|
||||
vec![LocalSource {
|
||||
local: self,
|
||||
source: source.map(|ast| Either::Right(ast.to_node(&root))),
|
||||
}]
|
||||
}
|
||||
_ => body[self.binding_id]
|
||||
.definitions
|
||||
.iter()
|
||||
.map(|&definition| {
|
||||
let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
|
||||
let root = src.file_syntax(db.upcast());
|
||||
LocalSource {
|
||||
local: self,
|
||||
source: src.map(|ast| match ast.to_node(&root) {
|
||||
ast::Pat::IdentPat(it) => Either::Left(it),
|
||||
_ => unreachable!("local with non ident-pattern"),
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = it;`
|
||||
pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
|
||||
let (body, source_map) = db.body_with_source_map(self.parent);
|
||||
let src = self.sources_(db, &body, &source_map).next().unwrap();
|
||||
src
|
||||
}
|
||||
|
||||
fn sources_<'a>(
|
||||
self,
|
||||
db: &'a dyn HirDatabase,
|
||||
body: &'a hir_def::body::Body,
|
||||
source_map: &'a hir_def::body::BodySourceMap,
|
||||
) -> impl Iterator<Item = LocalSource> + 'a {
|
||||
body[self.binding_id]
|
||||
.definitions
|
||||
.iter()
|
||||
.map(|&definition| {
|
||||
let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
|
||||
let root = src.file_syntax(db.upcast());
|
||||
src.map(|ast| match ast.to_node(&root) {
|
||||
Either::Left(ast::Pat::IdentPat(it)) => Either::Left(it),
|
||||
Either::Left(_) => unreachable!("local with non ident-pattern"),
|
||||
Either::Right(it) => Either::Right(it),
|
||||
match body.self_param.zip(source_map.self_param_syntax()) {
|
||||
Some((param, source)) if param == self.binding_id => {
|
||||
let root = source.file_syntax(db.upcast());
|
||||
LocalSource {
|
||||
local: self,
|
||||
source: source.map(|ast| Either::Right(ast.to_node(&root))),
|
||||
}
|
||||
}
|
||||
_ => body[self.binding_id]
|
||||
.definitions
|
||||
.first()
|
||||
.map(|&definition| {
|
||||
let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
|
||||
let root = src.file_syntax(db.upcast());
|
||||
LocalSource {
|
||||
local: self,
|
||||
source: src.map(|ast| match ast.to_node(&root) {
|
||||
ast::Pat::IdentPat(it) => Either::Left(it),
|
||||
_ => unreachable!("local with non ident-pattern"),
|
||||
}),
|
||||
}
|
||||
})
|
||||
})
|
||||
.map(move |source| LocalSource { local: self, source })
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4037,7 +4082,7 @@ impl Type {
|
|||
|
||||
let canonical_ty =
|
||||
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
|
||||
method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), trait_)
|
||||
method_resolution::implements_trait(&canonical_ty, db, &self.env, trait_)
|
||||
}
|
||||
|
||||
/// Checks that particular type `ty` implements `std::ops::FnOnce`.
|
||||
|
@ -4052,12 +4097,7 @@ impl Type {
|
|||
|
||||
let canonical_ty =
|
||||
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
|
||||
method_resolution::implements_trait_unique(
|
||||
&canonical_ty,
|
||||
db,
|
||||
self.env.clone(),
|
||||
fnonce_trait,
|
||||
)
|
||||
method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, fnonce_trait)
|
||||
}
|
||||
|
||||
// FIXME: Find better API that also handles const generics
|
||||
|
|
|
@ -681,28 +681,29 @@ impl<'db> SemanticsImpl<'db> {
|
|||
.filter(|&(_, include_file_id)| include_file_id == file_id)
|
||||
{
|
||||
let macro_file = invoc.as_macro_file();
|
||||
let expansion_info = cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
|
||||
let expansion_info = cache.entry(macro_file).or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
self.cache(value, file_id.into());
|
||||
|
||||
exp_info
|
||||
});
|
||||
|
||||
// Create the source analyzer for the macro call scope
|
||||
let Some(sa) = self.analyze_no_infer(&self.parse_or_expand(expansion_info.call_file()))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
{
|
||||
let InMacroFile { file_id: macro_file, value } = expansion_info.expanded();
|
||||
self.cache(value, macro_file.into());
|
||||
}
|
||||
|
||||
// get mapped token in the include! macro file
|
||||
let span = span::SpanData {
|
||||
let span = span::Span {
|
||||
range: token.text_range(),
|
||||
anchor: span::SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||
ctx: SyntaxContextId::ROOT,
|
||||
};
|
||||
let Some(InMacroFile { file_id, value: mut mapped_tokens }) =
|
||||
expansion_info.map_range_down(span)
|
||||
expansion_info.map_range_down_exact(span)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
@ -753,22 +754,20 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let def_map = sa.resolver.def_map();
|
||||
|
||||
let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])];
|
||||
|
||||
let mut process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
|
||||
let expansion_info = cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
|
||||
let exp_info = cache.entry(macro_file).or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
|
||||
{
|
||||
let InMacroFile { file_id, value } = expansion_info.expanded();
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
self.cache(value, file_id.into());
|
||||
}
|
||||
|
||||
let InMacroFile { file_id, value: mapped_tokens } =
|
||||
expansion_info.map_range_down(span)?;
|
||||
exp_info
|
||||
});
|
||||
|
||||
let InMacroFile { file_id, value: mapped_tokens } = exp_info.map_range_down(span)?;
|
||||
let mapped_tokens: SmallVec<[_; 2]> = mapped_tokens.collect();
|
||||
|
||||
// if the length changed we have found a mapping for the token
|
||||
// we have found a mapping for the token if the vec is non-empty
|
||||
let res = mapped_tokens.is_empty().not().then_some(());
|
||||
// requeue the tokens we got from mapping our current token down
|
||||
stack.push((HirFileId::from(file_id), mapped_tokens));
|
||||
|
@ -851,7 +850,13 @@ impl<'db> SemanticsImpl<'db> {
|
|||
// remove any other token in this macro input, all their mappings are the
|
||||
// same as this one
|
||||
tokens.retain(|t| !text_range.contains_range(t.text_range()));
|
||||
process_expansion_for_token(&mut stack, file_id)
|
||||
|
||||
process_expansion_for_token(&mut stack, file_id).or(file_id
|
||||
.eager_arg(self.db.upcast())
|
||||
.and_then(|arg| {
|
||||
// also descend into eager expansions
|
||||
process_expansion_for_token(&mut stack, arg.as_macro_file())
|
||||
}))
|
||||
} else if let Some(meta) = ast::Meta::cast(parent) {
|
||||
// attribute we failed expansion for earlier, this might be a derive invocation
|
||||
// or derive helper attribute
|
||||
|
@ -960,7 +965,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
/// macro file the node resides in.
|
||||
pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
|
||||
let node = self.find_file(node);
|
||||
node.original_file_range(self.db.upcast())
|
||||
node.original_file_range_rooted(self.db.upcast())
|
||||
}
|
||||
|
||||
/// Attempts to map the node out of macro expanded files returning the original file range.
|
||||
|
@ -984,9 +989,9 @@ impl<'db> SemanticsImpl<'db> {
|
|||
|
||||
/// Attempts to map the node out of macro expanded files.
|
||||
/// This only work for attribute expansions, as other ones do not have nodes as input.
|
||||
pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
|
||||
pub fn original_syntax_node_rooted(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
|
||||
let InFile { file_id, .. } = self.find_file(node);
|
||||
InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map(
|
||||
InFile::new(file_id, node).original_syntax_node_rooted(self.db.upcast()).map(
|
||||
|InRealFile { file_id, value }| {
|
||||
self.cache(find_root(&value), file_id.into());
|
||||
value
|
||||
|
@ -997,7 +1002,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
pub fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
|
||||
let root = self.parse_or_expand(src.file_id);
|
||||
let node = src.map(|it| it.to_node(&root));
|
||||
node.as_ref().original_file_range(self.db.upcast())
|
||||
node.as_ref().original_file_range_rooted(self.db.upcast())
|
||||
}
|
||||
|
||||
fn token_ancestors_with_macros(
|
||||
|
@ -1236,6 +1241,11 @@ impl<'db> SemanticsImpl<'db> {
|
|||
sa.resolve_macro_call(self.db, macro_call)
|
||||
}
|
||||
|
||||
pub fn is_proc_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
|
||||
self.resolve_macro_call(macro_call)
|
||||
.map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..)))
|
||||
}
|
||||
|
||||
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
|
||||
let sa = match self.analyze(macro_call.syntax()) {
|
||||
Some(it) => it,
|
||||
|
|
|
@ -101,7 +101,7 @@ use hir_def::{
|
|||
use hir_expand::{attrs::AttrId, name::AsName, HirFileId, HirFileIdExt, MacroCallId};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::{impl_from, never};
|
||||
use stdx::impl_from;
|
||||
use syntax::{
|
||||
ast::{self, HasName},
|
||||
AstNode, SyntaxNode,
|
||||
|
@ -253,14 +253,8 @@ impl SourceToDefCtx<'_, '_> {
|
|||
src: InFile<ast::SelfParam>,
|
||||
) -> Option<(DefWithBodyId, BindingId)> {
|
||||
let container = self.find_pat_or_label_container(src.syntax())?;
|
||||
let (body, source_map) = self.db.body_with_source_map(container);
|
||||
let pat_id = source_map.node_self_param(src.as_ref())?;
|
||||
if let crate::Pat::Bind { id, .. } = body[pat_id] {
|
||||
Some((container, id))
|
||||
} else {
|
||||
never!();
|
||||
None
|
||||
}
|
||||
let body = self.db.body(container);
|
||||
Some((container, body.self_param?))
|
||||
}
|
||||
pub(super) fn label_to_def(
|
||||
&mut self,
|
||||
|
|
|
@ -219,11 +219,10 @@ impl SourceAnalyzer {
|
|||
pub(crate) fn type_of_self(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
param: &ast::SelfParam,
|
||||
_param: &ast::SelfParam,
|
||||
) -> Option<Type> {
|
||||
let src = InFile { file_id: self.file_id, value: param };
|
||||
let pat_id = self.body_source_map()?.node_self_param(src)?;
|
||||
let ty = self.infer.as_ref()?[pat_id].clone();
|
||||
let binding = self.body()?.self_param?;
|
||||
let ty = self.infer.as_ref()?[binding].clone();
|
||||
Some(Type::new_with_resolver(db, &self.resolver, ty))
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ impl DeclarationLocation {
|
|||
return FileRange { file_id, range: self.ptr.text_range() };
|
||||
}
|
||||
let node = resolve_node(db, self.hir_file_id, &self.ptr);
|
||||
node.as_ref().original_file_range(db.upcast())
|
||||
node.as_ref().original_file_range_rooted(db.upcast())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,6 @@ impl<'a> SymbolCollector<'a> {
|
|||
// Record renamed imports.
|
||||
// FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily
|
||||
// for now.
|
||||
// FIXME: This parses!
|
||||
for id in scope.imports() {
|
||||
let source = id.import.child_source(self.db.upcast());
|
||||
let Some(use_tree_src) = source.value.get(id.idx) else { continue };
|
||||
|
@ -196,7 +195,7 @@ impl<'a> SymbolCollector<'a> {
|
|||
});
|
||||
}
|
||||
|
||||
for const_id in scope.unnamed_consts(self.db.upcast()) {
|
||||
for const_id in scope.unnamed_consts() {
|
||||
self.collect_from_body(const_id);
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -198,7 +198,7 @@ fn get_adt_source(
|
|||
adt: &hir::Adt,
|
||||
fn_name: &str,
|
||||
) -> Option<(Option<ast::Impl>, FileId)> {
|
||||
let range = adt.source(ctx.sema.db)?.syntax().original_file_range(ctx.sema.db);
|
||||
let range = adt.source(ctx.sema.db)?.syntax().original_file_range_rooted(ctx.sema.db);
|
||||
let file = ctx.sema.parse(range.file_id);
|
||||
let adt_source =
|
||||
ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?;
|
||||
|
|
|
@ -206,7 +206,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
|
|||
let fn_body = fn_source.value.body()?;
|
||||
let param_list = fn_source.value.param_list()?;
|
||||
|
||||
let FileRange { file_id, range } = fn_source.syntax().original_file_range(ctx.sema.db);
|
||||
let FileRange { file_id, range } = fn_source.syntax().original_file_range_rooted(ctx.sema.db);
|
||||
if file_id == ctx.file_id() && range.contains(ctx.offset()) {
|
||||
cov_mark::hit!(inline_call_recursive);
|
||||
return None;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
//! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html)
|
||||
use hir::Semantics;
|
||||
use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase};
|
||||
use syntax::ast::{self, IsString};
|
||||
use hir::MacroFileIdExt;
|
||||
use ide_db::syntax_helpers::node_ext::macro_call_for_string_token;
|
||||
use syntax::{
|
||||
ast::{self, IsString},
|
||||
AstToken,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind,
|
||||
|
@ -32,10 +35,24 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[
|
|||
pub(crate) fn complete_cargo_env_vars(
|
||||
acc: &mut Completions,
|
||||
ctx: &CompletionContext<'_>,
|
||||
original: &ast::String,
|
||||
expanded: &ast::String,
|
||||
) -> Option<()> {
|
||||
guard_env_macro(expanded, &ctx.sema)?;
|
||||
let range = expanded.text_range_between_quotes()?;
|
||||
let is_in_env_expansion = ctx
|
||||
.sema
|
||||
.hir_file_for(&expanded.syntax().parent()?)
|
||||
.macro_file()
|
||||
.map_or(false, |it| it.is_env_or_option_env(ctx.sema.db));
|
||||
if !is_in_env_expansion {
|
||||
let call = macro_call_for_string_token(expanded)?;
|
||||
let makro = ctx.sema.resolve_macro_call(&call)?;
|
||||
// We won't map into `option_env` as that generates `None` for non-existent env vars
|
||||
// so fall back to this lookup
|
||||
if !makro.is_env_or_option_env(ctx.sema.db) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let range = original.text_range_between_quotes()?;
|
||||
|
||||
CARGO_DEFINED_VARS.iter().for_each(|&(var, detail)| {
|
||||
let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
|
||||
|
@ -46,18 +63,6 @@ pub(crate) fn complete_cargo_env_vars(
|
|||
Some(())
|
||||
}
|
||||
|
||||
fn guard_env_macro(string: &ast::String, semantics: &Semantics<'_, RootDatabase>) -> Option<()> {
|
||||
let call = macro_call_for_string_token(string)?;
|
||||
let name = call.path()?.segment()?.name_ref()?;
|
||||
let makro = semantics.resolve_macro_call(&call)?;
|
||||
let db = semantics.db;
|
||||
|
||||
match name.text().as_str() {
|
||||
"env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_edit, completion_list};
|
||||
|
@ -68,7 +73,7 @@ mod tests {
|
|||
&format!(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! {macro_name} {{
|
||||
macro {macro_name} {{
|
||||
($var:literal) => {{ 0 }}
|
||||
}}
|
||||
|
||||
|
@ -80,7 +85,7 @@ mod tests {
|
|||
&format!(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! {macro_name} {{
|
||||
macro {macro_name} {{
|
||||
($var:literal) => {{ 0 }}
|
||||
}}
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ fn complete_trait_impl_name(
|
|||
.parent()
|
||||
}
|
||||
}?;
|
||||
let item = ctx.sema.original_syntax_node(&item)?;
|
||||
let item = ctx.sema.original_syntax_node_rooted(&item)?;
|
||||
// item -> ASSOC_ITEM_LIST -> IMPL
|
||||
let impl_def = ast::Impl::cast(item.parent()?.parent()?)?;
|
||||
let replacement_range = {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::iter;
|
||||
|
||||
use hir::{HirFileIdExt, Module, ModuleSource};
|
||||
use hir::{HirFileIdExt, Module};
|
||||
use ide_db::{
|
||||
base_db::{SourceDatabaseExt, VfsPath},
|
||||
FxHashSet, RootDatabase, SymbolKind,
|
||||
|
@ -57,7 +57,7 @@ pub(crate) fn complete_mod(
|
|||
.collect::<FxHashSet<_>>();
|
||||
|
||||
let module_declaration_file =
|
||||
current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
|
||||
current_module.declaration_source_range(ctx.db).map(|module_declaration_source_file| {
|
||||
module_declaration_source_file.file_id.original_file(ctx.db)
|
||||
});
|
||||
|
||||
|
@ -148,9 +148,7 @@ fn module_chain_to_containing_module_file(
|
|||
) -> Vec<Module> {
|
||||
let mut path =
|
||||
iter::successors(Some(current_module), |current_module| current_module.parent(db))
|
||||
.take_while(|current_module| {
|
||||
matches!(current_module.definition_source(db).value, ModuleSource::Module(_))
|
||||
})
|
||||
.take_while(|current_module| current_module.is_inline(db))
|
||||
.collect::<Vec<_>>();
|
||||
path.reverse();
|
||||
path
|
||||
|
|
|
@ -369,6 +369,7 @@ impl CompletionItemKind {
|
|||
SymbolKind::LifetimeParam => "lt",
|
||||
SymbolKind::Local => "lc",
|
||||
SymbolKind::Macro => "ma",
|
||||
SymbolKind::ProcMacro => "pm",
|
||||
SymbolKind::Module => "md",
|
||||
SymbolKind::SelfParam => "sp",
|
||||
SymbolKind::SelfType => "sy",
|
||||
|
|
|
@ -207,7 +207,7 @@ pub fn completions(
|
|||
CompletionAnalysis::String { original, expanded: Some(expanded) } => {
|
||||
completions::extern_abi::complete_extern_abi(acc, ctx, expanded);
|
||||
completions::format_string::format_string(acc, ctx, original, expanded);
|
||||
completions::env_vars::complete_cargo_env_vars(acc, ctx, expanded);
|
||||
completions::env_vars::complete_cargo_env_vars(acc, ctx, original, expanded);
|
||||
}
|
||||
CompletionAnalysis::UnexpandedAttrTT {
|
||||
colon_prefix,
|
||||
|
|
|
@ -205,6 +205,7 @@ impl RootDatabase {
|
|||
|
||||
// SourceDatabaseExt
|
||||
base_db::FileTextQuery
|
||||
base_db::CompressedFileTextQuery
|
||||
base_db::FileSourceRootQuery
|
||||
base_db::SourceRootQuery
|
||||
base_db::SourceRootCratesQuery
|
||||
|
|
|
@ -407,7 +407,7 @@ impl NameClass {
|
|||
}
|
||||
|
||||
pub fn classify(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<NameClass> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "classify_name").entered();
|
||||
let _p = tracing::span!(tracing::Level::INFO, "NameClass::classify").entered();
|
||||
|
||||
let parent = name.syntax().parent()?;
|
||||
|
||||
|
@ -499,7 +499,8 @@ impl NameClass {
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
lifetime: &ast::Lifetime,
|
||||
) -> Option<NameClass> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "classify_lifetime", ?lifetime).entered();
|
||||
let _p = tracing::span!(tracing::Level::INFO, "NameClass::classify_lifetime", ?lifetime)
|
||||
.entered();
|
||||
let parent = lifetime.syntax().parent()?;
|
||||
|
||||
if let Some(it) = ast::LifetimeParam::cast(parent.clone()) {
|
||||
|
@ -590,7 +591,8 @@ impl NameRefClass {
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
name_ref: &ast::NameRef,
|
||||
) -> Option<NameRefClass> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "classify_name_ref", ?name_ref).entered();
|
||||
let _p =
|
||||
tracing::span!(tracing::Level::INFO, "NameRefClass::classify", ?name_ref).entered();
|
||||
|
||||
let parent = name_ref.syntax().parent()?;
|
||||
|
||||
|
@ -689,7 +691,8 @@ impl NameRefClass {
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
lifetime: &ast::Lifetime,
|
||||
) -> Option<NameRefClass> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "classify_lifetime_ref", ?lifetime).entered();
|
||||
let _p = tracing::span!(tracing::Level::INFO, "NameRefClass::classify_lifetime", ?lifetime)
|
||||
.entered();
|
||||
let parent = lifetime.syntax().parent()?;
|
||||
match parent.kind() {
|
||||
SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
|
||||
|
|
|
@ -71,7 +71,7 @@ pub fn visit_file_defs(
|
|||
let mut defs: VecDeque<_> = module.declarations(db).into();
|
||||
while let Some(def) = defs.pop_front() {
|
||||
if let ModuleDef::Module(submodule) = def {
|
||||
if let hir::ModuleSource::Module(_) = submodule.definition_source(db).value {
|
||||
if submodule.is_inline(db) {
|
||||
defs.extend(submodule.declarations(db));
|
||||
submodule.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into()));
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ use std::{fmt, mem::ManuallyDrop};
|
|||
use base_db::{
|
||||
salsa::{self, Durability},
|
||||
AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
|
||||
DEFAULT_FILE_TEXT_LRU_CAP,
|
||||
};
|
||||
use hir::db::{DefDatabase, ExpandDatabase, HirDatabase};
|
||||
use triomphe::Arc;
|
||||
|
@ -157,6 +158,7 @@ impl RootDatabase {
|
|||
|
||||
pub fn update_base_query_lru_capacities(&mut self, lru_capacity: Option<usize>) {
|
||||
let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP);
|
||||
base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP);
|
||||
base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
|
||||
// macro expansions are usually rather small, so we can afford to keep more of them alive
|
||||
hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity);
|
||||
|
@ -166,6 +168,7 @@ impl RootDatabase {
|
|||
pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
|
||||
use hir::db as hir_db;
|
||||
|
||||
base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP);
|
||||
base_db::ParseQuery.in_db_mut(self).set_lru_capacity(
|
||||
lru_capacities
|
||||
.get(stringify!(ParseQuery))
|
||||
|
@ -199,7 +202,7 @@ impl RootDatabase {
|
|||
// base_db::ProcMacrosQuery
|
||||
|
||||
// SourceDatabaseExt
|
||||
// base_db::FileTextQuery
|
||||
base_db::FileTextQuery
|
||||
// base_db::FileSourceRootQuery
|
||||
// base_db::SourceRootQuery
|
||||
base_db::SourceRootCratesQuery
|
||||
|
@ -348,6 +351,7 @@ pub enum SymbolKind {
|
|||
LifetimeParam,
|
||||
Local,
|
||||
Macro,
|
||||
ProcMacro,
|
||||
Module,
|
||||
SelfParam,
|
||||
SelfType,
|
||||
|
@ -366,9 +370,8 @@ pub enum SymbolKind {
|
|||
impl From<hir::MacroKind> for SymbolKind {
|
||||
fn from(it: hir::MacroKind) -> Self {
|
||||
match it {
|
||||
hir::MacroKind::Declarative | hir::MacroKind::BuiltIn | hir::MacroKind::ProcMacro => {
|
||||
SymbolKind::Macro
|
||||
}
|
||||
hir::MacroKind::Declarative | hir::MacroKind::BuiltIn => SymbolKind::Macro,
|
||||
hir::MacroKind::ProcMacro => SymbolKind::ProcMacro,
|
||||
hir::MacroKind::Derive => SymbolKind::Derive,
|
||||
hir::MacroKind::Attr => SymbolKind::Attribute,
|
||||
}
|
||||
|
@ -381,6 +384,7 @@ impl From<hir::ModuleDefId> for SymbolKind {
|
|||
hir::ModuleDefId::ConstId(..) => SymbolKind::Const,
|
||||
hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant,
|
||||
hir::ModuleDefId::FunctionId(..) => SymbolKind::Function,
|
||||
hir::ModuleDefId::MacroId(hir::MacroId::ProcMacroId(..)) => SymbolKind::ProcMacro,
|
||||
hir::ModuleDefId::MacroId(..) => SymbolKind::Macro,
|
||||
hir::ModuleDefId::ModuleId(..) => SymbolKind::Module,
|
||||
hir::ModuleDefId::StaticId(..) => SymbolKind::Static,
|
||||
|
|
|
@ -190,22 +190,15 @@ impl SearchScope {
|
|||
let mut entries = IntMap::default();
|
||||
|
||||
let (file_id, range) = {
|
||||
let InFile { file_id, value } = module.definition_source(db);
|
||||
let InFile { file_id, value } = module.definition_source_range(db);
|
||||
if let Some(InRealFile { file_id, value: call_source }) = file_id.original_call_node(db)
|
||||
{
|
||||
(file_id, Some(call_source.text_range()))
|
||||
} else {
|
||||
(
|
||||
file_id.original_file(db),
|
||||
match value {
|
||||
ModuleSource::SourceFile(_) => None,
|
||||
ModuleSource::Module(it) => Some(it.syntax().text_range()),
|
||||
ModuleSource::BlockExpr(it) => Some(it.syntax().text_range()),
|
||||
},
|
||||
)
|
||||
(file_id.original_file(db), Some(value))
|
||||
}
|
||||
};
|
||||
entries.insert(file_id, range);
|
||||
entries.entry(file_id).or_insert(range);
|
||||
|
||||
let mut to_visit: Vec<_> = module.children(db).collect();
|
||||
while let Some(module) = to_visit.pop() {
|
||||
|
|
|
@ -38,7 +38,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Ass
|
|||
let def = NameClass::classify(&ctx.sema, &name_node)?.defined()?;
|
||||
|
||||
let name_node = InFile::new(d.file, name_node.syntax());
|
||||
let frange = name_node.original_file_range(ctx.sema.db);
|
||||
let frange = name_node.original_file_range_rooted(ctx.sema.db);
|
||||
|
||||
let label = format!("Rename to {}", d.suggested_text);
|
||||
let mut res = unresolved_fix("change_case", &label, frange.range);
|
||||
|
|
|
@ -413,7 +413,7 @@ fn main() {
|
|||
fn main() {
|
||||
return;
|
||||
let mut x = 2;
|
||||
//^^^^^ warn: unused variable
|
||||
//^^^^^ 💡 warn: unused variable
|
||||
&mut x;
|
||||
}
|
||||
"#,
|
||||
|
@ -423,7 +423,7 @@ fn main() {
|
|||
fn main() {
|
||||
loop {}
|
||||
let mut x = 2;
|
||||
//^^^^^ warn: unused variable
|
||||
//^^^^^ 💡 warn: unused variable
|
||||
&mut x;
|
||||
}
|
||||
"#,
|
||||
|
@ -444,7 +444,7 @@ fn main(b: bool) {
|
|||
g();
|
||||
}
|
||||
let mut x = 2;
|
||||
//^^^^^ warn: unused variable
|
||||
//^^^^^ 💡 warn: unused variable
|
||||
&mut x;
|
||||
}
|
||||
"#,
|
||||
|
@ -459,7 +459,7 @@ fn main(b: bool) {
|
|||
return;
|
||||
}
|
||||
let mut x = 2;
|
||||
//^^^^^ warn: unused variable
|
||||
//^^^^^ 💡 warn: unused variable
|
||||
&mut x;
|
||||
}
|
||||
"#,
|
||||
|
@ -789,7 +789,7 @@ fn f() {
|
|||
//^^ 💡 error: cannot mutate immutable variable `x`
|
||||
_ = (x, y);
|
||||
let x = Foo;
|
||||
//^ warn: unused variable
|
||||
//^ 💡 warn: unused variable
|
||||
let x = Foo;
|
||||
let y: &mut (i32, u8) = &mut x;
|
||||
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
use hir::{db::ExpandDatabase, HirDisplay, InFile};
|
||||
use std::iter;
|
||||
|
||||
use hir::{db::ExpandDatabase, Adt, HasSource, HirDisplay, InFile, Struct, Union};
|
||||
use ide_db::{
|
||||
assists::{Assist, AssistId, AssistKind},
|
||||
base_db::FileRange,
|
||||
helpers::is_editable_crate,
|
||||
label::Label,
|
||||
source_change::SourceChange,
|
||||
source_change::{SourceChange, SourceChangeBuilder},
|
||||
};
|
||||
use syntax::{
|
||||
algo,
|
||||
ast::{self, edit::IndentLevel, make, FieldList, Name, Visibility},
|
||||
AstNode, AstPtr, Direction, SyntaxKind, TextSize,
|
||||
};
|
||||
use syntax::{
|
||||
ast::{edit::AstNodeEdit, Type},
|
||||
SyntaxNode,
|
||||
};
|
||||
use syntax::{ast, AstNode, AstPtr};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||
|
@ -46,24 +57,206 @@ pub(crate) fn unresolved_field(
|
|||
}
|
||||
|
||||
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<Assist>> {
|
||||
let mut fixes = Vec::new();
|
||||
if d.method_with_same_name_exists {
|
||||
method_fix(ctx, &d.expr)
|
||||
} else {
|
||||
// FIXME: add quickfix
|
||||
|
||||
None
|
||||
fixes.extend(method_fix(ctx, &d.expr));
|
||||
}
|
||||
fixes.extend(field_fix(ctx, d));
|
||||
if fixes.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(fixes)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Add Snippet Support
|
||||
fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Assist> {
|
||||
// Get the FileRange of the invalid field access
|
||||
let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
|
||||
let expr = d.expr.value.to_node(&root);
|
||||
|
||||
let error_range = ctx.sema.original_range_opt(expr.syntax())?;
|
||||
let field_name = d.name.as_str()?;
|
||||
// Convert the receiver to an ADT
|
||||
let adt = d.receiver.strip_references().as_adt()?;
|
||||
let target_module = adt.module(ctx.sema.db);
|
||||
|
||||
let suggested_type =
|
||||
if let Some(new_field_type) = ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()) {
|
||||
let display =
|
||||
new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok();
|
||||
make::ty(display.as_deref().unwrap_or("()"))
|
||||
} else {
|
||||
make::ty("()")
|
||||
};
|
||||
|
||||
if !is_editable_crate(target_module.krate(), ctx.sema.db) {
|
||||
return None;
|
||||
}
|
||||
|
||||
match adt {
|
||||
Adt::Struct(adt_struct) => {
|
||||
add_field_to_struct_fix(ctx, adt_struct, field_name, suggested_type, error_range)
|
||||
}
|
||||
Adt::Union(adt_union) => {
|
||||
add_variant_to_union(ctx, adt_union, field_name, suggested_type, error_range)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_variant_to_union(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
adt_union: Union,
|
||||
field_name: &str,
|
||||
suggested_type: Type,
|
||||
error_range: FileRange,
|
||||
) -> Option<Assist> {
|
||||
let adt_source = adt_union.source(ctx.sema.db)?;
|
||||
let adt_syntax = adt_source.syntax();
|
||||
let field_list = adt_source.value.record_field_list()?;
|
||||
let range = adt_syntax.original_file_range_rooted(ctx.sema.db);
|
||||
let field_name = make::name(field_name);
|
||||
|
||||
let (offset, record_field) =
|
||||
record_field_layout(None, field_name, suggested_type, field_list, adt_syntax.value)?;
|
||||
|
||||
let mut src_change_builder = SourceChangeBuilder::new(range.file_id);
|
||||
src_change_builder.insert(offset, record_field);
|
||||
Some(Assist {
|
||||
id: AssistId("add-variant-to-union", AssistKind::QuickFix),
|
||||
label: Label::new("Add field to union".to_owned()),
|
||||
group: None,
|
||||
target: error_range.range,
|
||||
source_change: Some(src_change_builder.finish()),
|
||||
trigger_signature_help: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn add_field_to_struct_fix(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
adt_struct: Struct,
|
||||
field_name: &str,
|
||||
suggested_type: Type,
|
||||
error_range: FileRange,
|
||||
) -> Option<Assist> {
|
||||
let struct_source = adt_struct.source(ctx.sema.db)?;
|
||||
let struct_syntax = struct_source.syntax();
|
||||
let struct_range = struct_syntax.original_file_range_rooted(ctx.sema.db);
|
||||
let field_list = struct_source.value.field_list();
|
||||
match field_list {
|
||||
Some(FieldList::RecordFieldList(field_list)) => {
|
||||
// Get range of final field in the struct
|
||||
let visibility = if error_range.file_id == struct_range.file_id {
|
||||
None
|
||||
} else {
|
||||
Some(make::visibility_pub_crate())
|
||||
};
|
||||
let field_name = make::name(field_name);
|
||||
|
||||
let (offset, record_field) = record_field_layout(
|
||||
visibility,
|
||||
field_name,
|
||||
suggested_type,
|
||||
field_list,
|
||||
struct_syntax.value,
|
||||
)?;
|
||||
|
||||
let mut src_change_builder = SourceChangeBuilder::new(struct_range.file_id);
|
||||
|
||||
// FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563
|
||||
src_change_builder.insert(offset, record_field);
|
||||
Some(Assist {
|
||||
id: AssistId("add-field-to-record-struct", AssistKind::QuickFix),
|
||||
label: Label::new("Add field to Record Struct".to_owned()),
|
||||
group: None,
|
||||
target: error_range.range,
|
||||
source_change: Some(src_change_builder.finish()),
|
||||
trigger_signature_help: false,
|
||||
})
|
||||
}
|
||||
None => {
|
||||
// Add a field list to the Unit Struct
|
||||
let mut src_change_builder = SourceChangeBuilder::new(struct_range.file_id);
|
||||
let field_name = make::name(field_name);
|
||||
let visibility = if error_range.file_id == struct_range.file_id {
|
||||
None
|
||||
} else {
|
||||
Some(make::visibility_pub_crate())
|
||||
};
|
||||
// FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563
|
||||
let indent = IndentLevel::from_node(struct_syntax.value) + 1;
|
||||
|
||||
let field = make::record_field(visibility, field_name, suggested_type).indent(indent);
|
||||
let record_field_list = make::record_field_list(iter::once(field));
|
||||
// A Unit Struct with no `;` is invalid syntax. We should not suggest this fix.
|
||||
let semi_colon =
|
||||
algo::skip_trivia_token(struct_syntax.value.last_token()?, Direction::Prev)?;
|
||||
if semi_colon.kind() != SyntaxKind::SEMICOLON {
|
||||
return None;
|
||||
}
|
||||
src_change_builder.replace(semi_colon.text_range(), record_field_list.to_string());
|
||||
|
||||
Some(Assist {
|
||||
id: AssistId("convert-unit-struct-to-record-struct", AssistKind::QuickFix),
|
||||
label: Label::new("Convert Unit Struct to Record Struct and add field".to_owned()),
|
||||
group: None,
|
||||
target: error_range.range,
|
||||
source_change: Some(src_change_builder.finish()),
|
||||
trigger_signature_help: false,
|
||||
})
|
||||
}
|
||||
Some(FieldList::TupleFieldList(_tuple)) => {
|
||||
// FIXME: Add support for Tuple Structs. Tuple Structs are not sent to this diagnostic
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to determine the layout of the record field in the struct.
|
||||
fn record_field_layout(
|
||||
visibility: Option<Visibility>,
|
||||
name: Name,
|
||||
suggested_type: Type,
|
||||
field_list: ast::RecordFieldList,
|
||||
struct_syntax: &SyntaxNode,
|
||||
) -> Option<(TextSize, String)> {
|
||||
let (offset, needs_comma, trailing_new_line, indent) = match field_list.fields().last() {
|
||||
Some(record_field) => {
|
||||
let syntax = algo::skip_trivia_token(field_list.r_curly_token()?, Direction::Prev)?;
|
||||
|
||||
let last_field_syntax = record_field.syntax();
|
||||
let last_field_indent = IndentLevel::from_node(last_field_syntax);
|
||||
(
|
||||
last_field_syntax.text_range().end(),
|
||||
syntax.kind() != SyntaxKind::COMMA,
|
||||
false,
|
||||
last_field_indent,
|
||||
)
|
||||
}
|
||||
// Empty Struct. Add a field right before the closing brace
|
||||
None => {
|
||||
let indent = IndentLevel::from_node(struct_syntax) + 1;
|
||||
let offset = field_list.r_curly_token()?.text_range().start();
|
||||
(offset, false, true, indent)
|
||||
}
|
||||
};
|
||||
let comma = if needs_comma { ",\n" } else { "" };
|
||||
let trailing_new_line = if trailing_new_line { "\n" } else { "" };
|
||||
let record_field = make::record_field(visibility, name, suggested_type);
|
||||
|
||||
Some((offset, format!("{comma}{indent}{record_field}{trailing_new_line}")))
|
||||
}
|
||||
|
||||
// FIXME: We should fill out the call here, move the cursor and trigger signature help
|
||||
fn method_fix(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
expr_ptr: &InFile<AstPtr<ast::Expr>>,
|
||||
) -> Option<Vec<Assist>> {
|
||||
) -> Option<Assist> {
|
||||
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
|
||||
let expr = expr_ptr.value.to_node(&root);
|
||||
let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?;
|
||||
Some(vec![Assist {
|
||||
Some(Assist {
|
||||
id: AssistId("expected-field-found-method-call-fix", AssistKind::QuickFix),
|
||||
label: Label::new("Use parentheses to call the method".to_owned()),
|
||||
group: None,
|
||||
|
@ -73,13 +266,15 @@ fn method_fix(
|
|||
TextEdit::insert(range.end(), "()".to_owned()),
|
||||
)),
|
||||
trigger_signature_help: false,
|
||||
}])
|
||||
})
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
tests::{
|
||||
check_diagnostics, check_diagnostics_with_config, check_diagnostics_with_disabled,
|
||||
check_fix,
|
||||
},
|
||||
DiagnosticsConfig,
|
||||
};
|
||||
|
@ -168,4 +363,100 @@ fn foo() {
|
|||
config.disabled.insert("syntax-error".to_owned());
|
||||
check_diagnostics_with_config(config, "fn foo() { (). }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unresolved_field_fix_on_unit() {
|
||||
check_fix(
|
||||
r#"
|
||||
struct Foo;
|
||||
|
||||
fn foo() {
|
||||
Foo.bar$0;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo{ bar: () }
|
||||
|
||||
fn foo() {
|
||||
Foo.bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn unresolved_field_fix_on_empty() {
|
||||
check_fix(
|
||||
r#"
|
||||
struct Foo{
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let foo = Foo{};
|
||||
foo.bar$0;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo{
|
||||
bar: ()
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let foo = Foo{};
|
||||
foo.bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn unresolved_field_fix_on_struct() {
|
||||
check_fix(
|
||||
r#"
|
||||
struct Foo{
|
||||
a: i32
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let foo = Foo{a: 0};
|
||||
foo.bar$0;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo{
|
||||
a: i32,
|
||||
bar: ()
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let foo = Foo{a: 0};
|
||||
foo.bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn unresolved_field_fix_on_union() {
|
||||
check_fix(
|
||||
r#"
|
||||
union Foo{
|
||||
a: i32
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let foo = Foo{a: 0};
|
||||
foo.bar$0;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
union Foo{
|
||||
a: i32,
|
||||
bar: ()
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let foo = Foo{a: 0};
|
||||
foo.bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
use ide_db::{
|
||||
assists::{Assist, AssistId, AssistKind},
|
||||
base_db::FileRange,
|
||||
label::Label,
|
||||
source_change::SourceChange,
|
||||
};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||
|
||||
// Diagnostic: unused-variables
|
||||
|
@ -8,18 +16,38 @@ pub(crate) fn unused_variables(
|
|||
d: &hir::UnusedVariable,
|
||||
) -> Diagnostic {
|
||||
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
|
||||
let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
|
||||
let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string();
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcLint("unused_variables"),
|
||||
"unused variable",
|
||||
ast,
|
||||
)
|
||||
.with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro()))
|
||||
.experimental()
|
||||
}
|
||||
|
||||
fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option<Vec<Assist>> {
|
||||
if is_in_marco {
|
||||
return None;
|
||||
}
|
||||
Some(vec![Assist {
|
||||
id: AssistId("unscore_unused_variable_name", AssistKind::QuickFix),
|
||||
label: Label::new(format!("Rename unused {} to _{}", var_name, var_name)),
|
||||
group: None,
|
||||
target: diagnostic_range.range,
|
||||
source_change: Some(SourceChange::from_text_edit(
|
||||
diagnostic_range.file_id,
|
||||
TextEdit::replace(diagnostic_range.range, format!("_{}", var_name)),
|
||||
)),
|
||||
trigger_signature_help: false,
|
||||
}])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::check_diagnostics;
|
||||
use crate::tests::{check_diagnostics, check_fix, check_no_fix};
|
||||
|
||||
#[test]
|
||||
fn unused_variables_simple() {
|
||||
|
@ -29,23 +57,23 @@ mod tests {
|
|||
struct Foo { f1: i32, f2: i64 }
|
||||
|
||||
fn f(kkk: i32) {}
|
||||
//^^^ warn: unused variable
|
||||
//^^^ 💡 warn: unused variable
|
||||
fn main() {
|
||||
let a = 2;
|
||||
//^ warn: unused variable
|
||||
//^ 💡 warn: unused variable
|
||||
let b = 5;
|
||||
// note: `unused variable` implies `unused mut`, so we should not emit both at the same time.
|
||||
let mut c = f(b);
|
||||
//^^^^^ warn: unused variable
|
||||
//^^^^^ 💡 warn: unused variable
|
||||
let (d, e) = (3, 5);
|
||||
//^ warn: unused variable
|
||||
//^ 💡 warn: unused variable
|
||||
let _ = e;
|
||||
let f1 = 2;
|
||||
let f2 = 5;
|
||||
let f = Foo { f1, f2 };
|
||||
match f {
|
||||
Foo { f1, f2 } => {
|
||||
//^^ warn: unused variable
|
||||
//^^ 💡 warn: unused variable
|
||||
_ = f2;
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +81,7 @@ fn main() {
|
|||
if g {}
|
||||
let h: fn() -> i32 = || 2;
|
||||
let i = h();
|
||||
//^ warn: unused variable
|
||||
//^ 💡 warn: unused variable
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
@ -67,11 +95,11 @@ struct S {
|
|||
}
|
||||
impl S {
|
||||
fn owned_self(self, u: i32) {}
|
||||
//^ warn: unused variable
|
||||
//^ 💡 warn: unused variable
|
||||
fn ref_self(&self, u: i32) {}
|
||||
//^ warn: unused variable
|
||||
//^ 💡 warn: unused variable
|
||||
fn ref_mut_self(&mut self, u: i32) {}
|
||||
//^ warn: unused variable
|
||||
//^ 💡 warn: unused variable
|
||||
fn owned_mut_self(mut self) {}
|
||||
//^^^^^^^^ 💡 warn: variable does not need to be mutable
|
||||
|
||||
|
@ -103,7 +131,78 @@ fn main() {
|
|||
#[deny(unused)]
|
||||
fn main2() {
|
||||
let x = 2;
|
||||
//^ error: unused variable
|
||||
//^ 💡 error: unused variable
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_unused_variable() {
|
||||
check_fix(
|
||||
r#"
|
||||
fn main() {
|
||||
let x$0 = 2;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let _x = 2;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
fn main() {
|
||||
let ($0d, _e) = (3, 5);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let (_d, _e) = (3, 5);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct Foo { f1: i32, f2: i64 }
|
||||
fn main() {
|
||||
let f = Foo { f1: 0, f2: 0 };
|
||||
match f {
|
||||
Foo { f1$0, f2 } => {
|
||||
_ = f2;
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo { f1: i32, f2: i64 }
|
||||
fn main() {
|
||||
let f = Foo { f1: 0, f2: 0 };
|
||||
match f {
|
||||
Foo { _f1, f2 } => {
|
||||
_ = f2;
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_fix_for_marco() {
|
||||
check_no_fix(
|
||||
r#"
|
||||
macro_rules! my_macro {
|
||||
() => {
|
||||
let x = 3;
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
$0my_macro!();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
|
@ -125,9 +125,12 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
|
|||
let match_state = Matcher { sema, restrict_range: *restrict_range, rule };
|
||||
// First pass at matching, where we check that node types and idents match.
|
||||
match_state.attempt_match_node(&mut Phase::First, &rule.pattern.node, code)?;
|
||||
match_state.validate_range(&sema.original_range(code))?;
|
||||
let file_range = sema
|
||||
.original_range_opt(code)
|
||||
.ok_or(MatchFailed { reason: Some("def site definition".to_owned()) })?;
|
||||
match_state.validate_range(&file_range)?;
|
||||
let mut the_match = Match {
|
||||
range: sema.original_range(code),
|
||||
range: file_range,
|
||||
matched_node: code.clone(),
|
||||
placeholder_values: FxHashMap::default(),
|
||||
ignored_comments: Vec::new(),
|
||||
|
@ -175,7 +178,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
|
|||
self.check_constraint(constraint, code)?;
|
||||
}
|
||||
if let Phase::Second(matches_out) = phase {
|
||||
let original_range = self.sema.original_range(code);
|
||||
let original_range = self
|
||||
.sema
|
||||
.original_range_opt(code)
|
||||
.ok_or(MatchFailed { reason: Some("def site definition".to_owned()) })?;
|
||||
// We validated the range for the node when we started the match, so the placeholder
|
||||
// probably can't fail range validation, but just to be safe...
|
||||
self.validate_range(&original_range)?;
|
||||
|
@ -487,7 +493,13 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
|
|||
match_out.placeholder_values.insert(
|
||||
placeholder.ident.clone(),
|
||||
PlaceholderMatch::from_range(FileRange {
|
||||
file_id: self.sema.original_range(code).file_id,
|
||||
file_id: self
|
||||
.sema
|
||||
.original_range_opt(code)
|
||||
.ok_or(MatchFailed {
|
||||
reason: Some("def site definition".to_owned()),
|
||||
})?
|
||||
.file_id,
|
||||
range: first_matched_token
|
||||
.text_range()
|
||||
.cover(last_matched_token.text_range()),
|
||||
|
|
|
@ -190,12 +190,9 @@ impl MatchFinder<'_> {
|
|||
// When matching within a macro expansion, we only want to allow matches of
|
||||
// nodes that originated entirely from within the token tree of the macro call.
|
||||
// i.e. we don't want to match something that came from the macro itself.
|
||||
self.slow_scan_node(
|
||||
&expanded,
|
||||
rule,
|
||||
&Some(self.sema.original_range(tt.syntax())),
|
||||
matches_out,
|
||||
);
|
||||
if let Some(range) = self.sema.original_range_opt(tt.syntax()) {
|
||||
self.slow_scan_node(&expanded, rule, &Some(range), matches_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +224,7 @@ impl MatchFinder<'_> {
|
|||
// There is no range restriction.
|
||||
return true;
|
||||
}
|
||||
let node_range = self.sema.original_range(code);
|
||||
let Some(node_range) = self.sema.original_range_opt(code) else { return false };
|
||||
for range in &self.restrict_ranges {
|
||||
if range.file_id == node_range.file_id && range.range.contains_range(node_range.range) {
|
||||
return true;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::mem::discriminant;
|
||||
use std::{iter, mem::discriminant};
|
||||
|
||||
use crate::{
|
||||
doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget,
|
||||
RangeInfo, TryToNav,
|
||||
};
|
||||
use hir::{AsAssocItem, AssocItem, DescendPreference, ModuleDef, Semantics};
|
||||
use hir::{AsAssocItem, AssocItem, DescendPreference, MacroFileIdExt, ModuleDef, Semantics};
|
||||
use ide_db::{
|
||||
base_db::{AnchoredPath, FileId, FileLoader},
|
||||
defs::{Definition, IdentClass},
|
||||
|
@ -74,11 +74,13 @@ pub(crate) fn goto_definition(
|
|||
.filter_map(|token| {
|
||||
let parent = token.parent()?;
|
||||
|
||||
if let Some(tt) = ast::TokenTree::cast(parent.clone()) {
|
||||
if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) {
|
||||
if let Some(token) = ast::String::cast(token.clone()) {
|
||||
if let Some(x) = try_lookup_include_path(sema, token, file_id) {
|
||||
return Some(vec![x]);
|
||||
}
|
||||
}
|
||||
|
||||
if ast::TokenTree::can_cast(parent.kind()) {
|
||||
if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token) {
|
||||
return Some(vec![x]);
|
||||
}
|
||||
|
@ -111,24 +113,17 @@ pub(crate) fn goto_definition(
|
|||
|
||||
fn try_lookup_include_path(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
tt: ast::TokenTree,
|
||||
token: SyntaxToken,
|
||||
token: ast::String,
|
||||
file_id: FileId,
|
||||
) -> Option<NavigationTarget> {
|
||||
let token = ast::String::cast(token)?;
|
||||
let path = token.value()?.into_owned();
|
||||
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
||||
let name = macro_call.path()?.segment()?.name_ref()?;
|
||||
if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
|
||||
let file = sema.hir_file_for(&token.syntax().parent()?).macro_file()?;
|
||||
if !iter::successors(Some(file), |file| file.parent(sema.db).macro_file())
|
||||
// Check that we are in the eager argument expansion of an include macro
|
||||
.any(|file| file.is_include_like_macro(sema.db) && file.eager_arg(sema.db).is_none())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
// Ignore non-built-in macros to account for shadowing
|
||||
if let Some(it) = sema.resolve_macro_call(¯o_call) {
|
||||
if !matches!(it.kind(sema.db), hir::MacroKind::BuiltIn) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let path = token.value()?;
|
||||
|
||||
let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
|
||||
let size = sema.db.file_text(file_id).len().try_into().ok()?;
|
||||
|
@ -1531,6 +1526,26 @@ fn main() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_include_has_eager_input() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! include_str {}
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! concat {}
|
||||
|
||||
fn main() {
|
||||
let str = include_str!(concat!("foo", ".tx$0t"));
|
||||
}
|
||||
//- /foo.txt
|
||||
// empty
|
||||
//^file
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_doc_include_str() {
|
||||
check(
|
||||
|
|
|
@ -510,7 +510,7 @@ fn render_notable_trait_comment(
|
|||
let mut needs_impl_header = true;
|
||||
for (trait_, assoc_types) in notable_traits {
|
||||
desc.push_str(if mem::take(&mut needs_impl_header) {
|
||||
" // Implements notable traits: "
|
||||
"// Implements notable traits: "
|
||||
} else {
|
||||
", "
|
||||
});
|
||||
|
@ -661,7 +661,7 @@ fn closure_ty(
|
|||
if let Some(layout) =
|
||||
render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None)
|
||||
{
|
||||
format_to!(markup, "{layout}");
|
||||
format_to!(markup, " {layout}");
|
||||
}
|
||||
if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) {
|
||||
push_new_def(hir::Trait::from(trait_).into())
|
||||
|
@ -730,7 +730,7 @@ fn render_memory_layout(
|
|||
let config = config?;
|
||||
let layout = layout().ok()?;
|
||||
|
||||
let mut label = String::from(" // ");
|
||||
let mut label = String::from("// ");
|
||||
|
||||
if let Some(render) = config.size {
|
||||
let size = match tag(&layout) {
|
||||
|
|
|
@ -180,7 +180,7 @@ fn foo() {
|
|||
*local*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
let local: i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -471,7 +471,7 @@ fn main() {
|
|||
*iter*
|
||||
|
||||
```rust
|
||||
// size = 8, align = 4
|
||||
// size = 8, align = 4
|
||||
let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, impl Fn(&mut u32, &u32, &mut u32) -> Option<u32>, u32>>
|
||||
```
|
||||
"#]],
|
||||
|
@ -713,7 +713,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 1, align = 1, offset = 6
|
||||
// size = 1, align = 1, offset = 6
|
||||
field_a: u8
|
||||
```
|
||||
"#]],
|
||||
|
@ -739,7 +739,7 @@ fn main() {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4, offset = 0
|
||||
// size = 4, align = 4, offset = 0
|
||||
pub field_a: u32
|
||||
```
|
||||
"#]],
|
||||
|
@ -762,7 +762,7 @@ fn main() {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4, offset = 0
|
||||
// size = 4, align = 4, offset = 0
|
||||
pub field_a: u32
|
||||
```
|
||||
"#]],
|
||||
|
@ -787,7 +787,7 @@ fn main() {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4, offset = 0
|
||||
// size = 4, align = 4, offset = 0
|
||||
pub 0: u32
|
||||
```
|
||||
"#]],
|
||||
|
@ -808,7 +808,7 @@ fn foo(foo: Foo) {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4, offset = 0
|
||||
// size = 4, align = 4, offset = 0
|
||||
pub 0: u32
|
||||
```
|
||||
"#]],
|
||||
|
@ -819,7 +819,7 @@ fn foo(foo: Foo) {
|
|||
fn hover_tuple_struct() {
|
||||
check(
|
||||
r#"
|
||||
struct Foo$0(pub u32)
|
||||
struct Foo$0(pub u32) where u32: Copy;
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
@ -829,8 +829,100 @@ struct Foo$0(pub u32)
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
struct Foo(pub u32);
|
||||
// size = 4, align = 4
|
||||
struct Foo(pub u32)
|
||||
where
|
||||
u32: Copy,
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_record_struct() {
|
||||
check(
|
||||
r#"
|
||||
struct Foo$0 { field: u32 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
struct Foo {
|
||||
field: u32,
|
||||
}
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
struct Foo$0 where u32: Copy { field: u32 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
struct Foo
|
||||
where
|
||||
u32: Copy,
|
||||
{
|
||||
field: u32,
|
||||
}
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_unit_struct() {
|
||||
check(
|
||||
r#"
|
||||
struct Foo$0 where u32: Copy;
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
struct Foo
|
||||
where
|
||||
u32: Copy,
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_type_alias() {
|
||||
check(
|
||||
r#"
|
||||
type Fo$0o: Trait = S where T: Trait;
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
type Foo: Trait = S
|
||||
where
|
||||
T: Trait,
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
|
@ -957,7 +1049,7 @@ fn main() {
|
|||
*zz*
|
||||
|
||||
```rust
|
||||
// size = 8, align = 4
|
||||
// size = 8, align = 4
|
||||
let zz: Test<i32>
|
||||
```
|
||||
"#]],
|
||||
|
@ -1009,7 +1101,7 @@ fn main() { let b$0ar = Some(12); }
|
|||
*bar*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
let bar: Option<i32>
|
||||
```
|
||||
"#]],
|
||||
|
@ -1079,7 +1171,7 @@ fn hover_for_local_variable() {
|
|||
*foo*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
foo: i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -1094,7 +1186,7 @@ fn hover_for_local_variable_pat() {
|
|||
*foo*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
foo: i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -1109,7 +1201,7 @@ fn hover_local_var_edge() {
|
|||
*foo*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
foo: i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -1124,7 +1216,7 @@ fn hover_for_param_edge() {
|
|||
*foo*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
foo: i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -1169,7 +1261,7 @@ fn main() { let foo_$0test = Thing::new(); }
|
|||
*foo_test*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
let foo_test: Thing
|
||||
```
|
||||
"#]],
|
||||
|
@ -1374,7 +1466,7 @@ fn y() {
|
|||
*x*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
let x: i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -1505,7 +1597,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
|
|||
*bar*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
bar: u32
|
||||
```
|
||||
"#]],
|
||||
|
@ -1524,7 +1616,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
|
|||
*bar*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
bar: u32
|
||||
```
|
||||
"#]],
|
||||
|
@ -1760,7 +1852,7 @@ fn test_hover_function_pointer_show_identifiers() {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 8, align = 8, niches = 1
|
||||
// size = 8, align = 8, niches = 1
|
||||
type foo = fn(a: i32, b: i32) -> i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -1779,7 +1871,7 @@ fn test_hover_function_pointer_no_identifier() {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 8, align = 8, niches = 1
|
||||
// size = 8, align = 8, niches = 1
|
||||
type foo = fn(i32, i32) -> i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -1926,7 +2018,7 @@ fn foo() { let bar = Ba$0r; }
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
struct Bar
|
||||
```
|
||||
|
||||
|
@ -1963,7 +2055,7 @@ fn foo() { let bar = Ba$0r; }
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
struct Bar
|
||||
```
|
||||
|
||||
|
@ -1993,7 +2085,7 @@ fn foo() { let bar = Ba$0r; }
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
struct Bar
|
||||
```
|
||||
|
||||
|
@ -2022,7 +2114,7 @@ pub struct B$0ar
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
pub struct Bar
|
||||
```
|
||||
|
||||
|
@ -2050,7 +2142,7 @@ pub struct B$0ar
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
pub struct Bar
|
||||
```
|
||||
|
||||
|
@ -2140,7 +2232,7 @@ fn test_hover_layout_of_variant() {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 2
|
||||
// size = 4, align = 2
|
||||
Variant1(u8, u16)
|
||||
```
|
||||
"#]],
|
||||
|
@ -2162,7 +2254,7 @@ fn test_hover_layout_of_enum() {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 16 (0x10), align = 8, niches = 254
|
||||
// size = 16 (0x10), align = 8, niches = 254
|
||||
enum Foo {
|
||||
Variant1(u8, u16),
|
||||
Variant2(i32, u8, i64),
|
||||
|
@ -2540,7 +2632,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
|
|||
focus_range: 7..10,
|
||||
name: "Arg",
|
||||
kind: Struct,
|
||||
description: "struct Arg(u32);",
|
||||
description: "struct Arg(u32)",
|
||||
},
|
||||
},
|
||||
HoverGotoTypeData {
|
||||
|
@ -2599,7 +2691,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
|
|||
focus_range: 7..10,
|
||||
name: "Arg",
|
||||
kind: Struct,
|
||||
description: "struct Arg(u32);",
|
||||
description: "struct Arg(u32)",
|
||||
},
|
||||
},
|
||||
HoverGotoTypeData {
|
||||
|
@ -2648,7 +2740,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
|
|||
focus_range: 7..8,
|
||||
name: "A",
|
||||
kind: Struct,
|
||||
description: "struct A(u32);",
|
||||
description: "struct A(u32)",
|
||||
},
|
||||
},
|
||||
HoverGotoTypeData {
|
||||
|
@ -2661,7 +2753,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
|
|||
focus_range: 22..23,
|
||||
name: "B",
|
||||
kind: Struct,
|
||||
description: "struct B(u32);",
|
||||
description: "struct B(u32)",
|
||||
},
|
||||
},
|
||||
HoverGotoTypeData {
|
||||
|
@ -2675,7 +2767,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
|
|||
name: "C",
|
||||
kind: Struct,
|
||||
container_name: "M",
|
||||
description: "pub struct C(u32);",
|
||||
description: "pub struct C(u32)",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -3331,26 +3423,26 @@ struct Foo<const BAR: Bar>;
|
|||
impl<const BAR: Bar> Foo<BAR$0> {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
[
|
||||
GoToType(
|
||||
[
|
||||
HoverGotoTypeData {
|
||||
mod_path: "test::Bar",
|
||||
nav: NavigationTarget {
|
||||
file_id: FileId(
|
||||
0,
|
||||
),
|
||||
full_range: 0..11,
|
||||
focus_range: 7..10,
|
||||
name: "Bar",
|
||||
kind: Struct,
|
||||
description: "struct Bar",
|
||||
},
|
||||
[
|
||||
GoToType(
|
||||
[
|
||||
HoverGotoTypeData {
|
||||
mod_path: "test::Bar",
|
||||
nav: NavigationTarget {
|
||||
file_id: FileId(
|
||||
0,
|
||||
),
|
||||
full_range: 0..11,
|
||||
focus_range: 7..10,
|
||||
name: "Bar",
|
||||
kind: Struct,
|
||||
description: "struct Bar",
|
||||
},
|
||||
],
|
||||
),
|
||||
]
|
||||
"#]],
|
||||
},
|
||||
],
|
||||
),
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3396,26 +3488,26 @@ impl Foo {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
[
|
||||
GoToType(
|
||||
[
|
||||
HoverGotoTypeData {
|
||||
mod_path: "test::Foo",
|
||||
nav: NavigationTarget {
|
||||
file_id: FileId(
|
||||
0,
|
||||
),
|
||||
full_range: 0..11,
|
||||
focus_range: 7..10,
|
||||
name: "Foo",
|
||||
kind: Struct,
|
||||
description: "struct Foo",
|
||||
},
|
||||
[
|
||||
GoToType(
|
||||
[
|
||||
HoverGotoTypeData {
|
||||
mod_path: "test::Foo",
|
||||
nav: NavigationTarget {
|
||||
file_id: FileId(
|
||||
0,
|
||||
),
|
||||
full_range: 0..11,
|
||||
focus_range: 7..10,
|
||||
name: "Foo",
|
||||
kind: Struct,
|
||||
description: "struct Foo",
|
||||
},
|
||||
],
|
||||
),
|
||||
]
|
||||
"#]],
|
||||
},
|
||||
],
|
||||
),
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3466,7 +3558,7 @@ fn main() {
|
|||
*f*
|
||||
|
||||
```rust
|
||||
// size = 8, align = 8, niches = 1
|
||||
// size = 8, align = 8, niches = 1
|
||||
let f: &i32
|
||||
```
|
||||
---
|
||||
|
@ -3476,7 +3568,7 @@ fn main() {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4, offset = 0
|
||||
// size = 4, align = 4, offset = 0
|
||||
f: i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -3498,7 +3590,7 @@ struct S$0T<const C: usize = 1, T = Foo>(T);
|
|||
```
|
||||
|
||||
```rust
|
||||
struct ST<const C: usize = 1, T = Foo>(T);
|
||||
struct ST<const C: usize = 1, T = Foo>(T)
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
|
@ -3519,7 +3611,7 @@ struct S$0T<const C: usize = {40 + 2}, T = Foo>(T);
|
|||
```
|
||||
|
||||
```rust
|
||||
struct ST<const C: usize = {const}, T = Foo>(T);
|
||||
struct ST<const C: usize = {const}, T = Foo>(T)
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
|
@ -3541,7 +3633,7 @@ struct S$0T<const C: usize = VAL, T = Foo>(T);
|
|||
```
|
||||
|
||||
```rust
|
||||
struct ST<const C: usize = VAL, T = Foo>(T);
|
||||
struct ST<const C: usize = VAL, T = Foo>(T)
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
|
@ -3561,7 +3653,7 @@ fn main() {
|
|||
*value*
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
let value: Const<1>
|
||||
```
|
||||
"#]],
|
||||
|
@ -3582,7 +3674,7 @@ fn main() {
|
|||
*value*
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
let value: Const<0>
|
||||
```
|
||||
"#]],
|
||||
|
@ -3603,7 +3695,7 @@ fn main() {
|
|||
*value*
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
let value: Const<-1>
|
||||
```
|
||||
"#]],
|
||||
|
@ -3624,7 +3716,7 @@ fn main() {
|
|||
*value*
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
let value: Const<true>
|
||||
```
|
||||
"#]],
|
||||
|
@ -3645,7 +3737,7 @@ fn main() {
|
|||
*value*
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
let value: Const<'🦀'>
|
||||
```
|
||||
"#]],
|
||||
|
@ -3665,7 +3757,7 @@ impl Foo {
|
|||
*self*
|
||||
|
||||
```rust
|
||||
// size = 8, align = 8, niches = 1
|
||||
// size = 8, align = 8, niches = 1
|
||||
self: &Foo
|
||||
```
|
||||
"#]],
|
||||
|
@ -3686,7 +3778,7 @@ impl Foo {
|
|||
*self*
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
self: Arc<Foo>
|
||||
```
|
||||
"#]],
|
||||
|
@ -4072,7 +4164,7 @@ type Fo$0o2 = Foo<2>;
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
type Foo2 = Foo<2>
|
||||
```
|
||||
"#]],
|
||||
|
@ -4115,7 +4207,7 @@ enum E {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 1, align = 1
|
||||
// size = 1, align = 1
|
||||
A = 8
|
||||
```
|
||||
|
||||
|
@ -4141,7 +4233,7 @@ enum E {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 1, align = 1
|
||||
// size = 1, align = 1
|
||||
A = 12 (0xC)
|
||||
```
|
||||
|
||||
|
@ -4168,7 +4260,7 @@ enum E {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 1, align = 1
|
||||
// size = 1, align = 1
|
||||
B = 2
|
||||
```
|
||||
|
||||
|
@ -4195,7 +4287,7 @@ enum E {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 1, align = 1
|
||||
// size = 1, align = 1
|
||||
B = 5
|
||||
```
|
||||
|
||||
|
@ -5002,7 +5094,7 @@ fn foo(e: E) {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
A = 3
|
||||
```
|
||||
|
||||
|
@ -5025,7 +5117,7 @@ fn main() {
|
|||
*tile4*
|
||||
|
||||
```rust
|
||||
// size = 32 (0x20), align = 4
|
||||
// size = 32 (0x20), align = 4
|
||||
let tile4: [u32; 8]
|
||||
```
|
||||
"#]],
|
||||
|
@ -5262,7 +5354,7 @@ pub fn gimme() -> theitem::TheItem {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
pub struct TheItem
|
||||
```
|
||||
|
||||
|
@ -5411,7 +5503,7 @@ mod string {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
struct String
|
||||
```
|
||||
|
||||
|
@ -5931,26 +6023,26 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
[
|
||||
GoToType(
|
||||
[
|
||||
HoverGotoTypeData {
|
||||
mod_path: "test::Foo",
|
||||
nav: NavigationTarget {
|
||||
file_id: FileId(
|
||||
0,
|
||||
),
|
||||
full_range: 0..11,
|
||||
focus_range: 7..10,
|
||||
name: "Foo",
|
||||
kind: Struct,
|
||||
description: "struct Foo",
|
||||
},
|
||||
[
|
||||
GoToType(
|
||||
[
|
||||
HoverGotoTypeData {
|
||||
mod_path: "test::Foo",
|
||||
nav: NavigationTarget {
|
||||
file_id: FileId(
|
||||
0,
|
||||
),
|
||||
full_range: 0..11,
|
||||
focus_range: 7..10,
|
||||
name: "Foo",
|
||||
kind: Struct,
|
||||
description: "struct Foo",
|
||||
},
|
||||
],
|
||||
),
|
||||
]
|
||||
"#]],
|
||||
},
|
||||
],
|
||||
),
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6139,7 +6231,7 @@ foo_macro!(
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
pub struct Foo
|
||||
```
|
||||
|
||||
|
@ -6165,8 +6257,8 @@ pub struct Foo(i32);
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
pub struct Foo(i32);
|
||||
// size = 4, align = 4
|
||||
pub struct Foo(i32)
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -6191,7 +6283,7 @@ pub struct Foo<T>(T);
|
|||
```
|
||||
|
||||
```rust
|
||||
pub struct Foo<T>(T);
|
||||
pub struct Foo<T>(T)
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -6290,7 +6382,7 @@ enum Enum {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
RecordV { field: u32 }
|
||||
```
|
||||
"#]],
|
||||
|
@ -6313,7 +6405,7 @@ enum Enum {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
field: u32
|
||||
```
|
||||
"#]],
|
||||
|
@ -6961,7 +7053,7 @@ fn test() {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4, offset = 0
|
||||
// size = 4, align = 4, offset = 0
|
||||
f: u32
|
||||
```
|
||||
"#]],
|
||||
|
@ -6981,7 +7073,7 @@ fn test() {
|
|||
*s*
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
let s: S
|
||||
```
|
||||
"#]],
|
||||
|
@ -7002,7 +7094,7 @@ fn test() {
|
|||
*foo*
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
// size = 4, align = 4
|
||||
let foo: i32
|
||||
```
|
||||
"#]],
|
||||
|
@ -7023,7 +7115,7 @@ format_args!("{aaaaa$0}");
|
|||
*aaaaa*
|
||||
|
||||
```rust
|
||||
// size = 16 (0x10), align = 8, niches = 1
|
||||
// size = 16 (0x10), align = 8, niches = 1
|
||||
let aaaaa: &str
|
||||
```
|
||||
"#]],
|
||||
|
@ -7044,7 +7136,7 @@ format_args!("{$0aaaaa}");
|
|||
*aaaaa*
|
||||
|
||||
```rust
|
||||
// size = 16 (0x10), align = 8, niches = 1
|
||||
// size = 16 (0x10), align = 8, niches = 1
|
||||
let aaaaa: &str
|
||||
```
|
||||
"#]],
|
||||
|
@ -7065,7 +7157,7 @@ format_args!(r"{$0aaaaa}");
|
|||
*aaaaa*
|
||||
|
||||
```rust
|
||||
// size = 16 (0x10), align = 8, niches = 1
|
||||
// size = 16 (0x10), align = 8, niches = 1
|
||||
let aaaaa: &str
|
||||
```
|
||||
"#]],
|
||||
|
@ -7091,7 +7183,7 @@ foo!(r"{$0aaaaa}");
|
|||
*aaaaa*
|
||||
|
||||
```rust
|
||||
// size = 16 (0x10), align = 8, niches = 1
|
||||
// size = 16 (0x10), align = 8, niches = 1
|
||||
let aaaaa: &str
|
||||
```
|
||||
"#]],
|
||||
|
@ -7440,8 +7532,8 @@ fn main(notable$0: u32) {}
|
|||
*notable*
|
||||
|
||||
```rust
|
||||
// Implements notable traits: Notable<Assoc = &str, Assoc2 = char>
|
||||
// size = 4, align = 4
|
||||
// Implements notable traits: Notable<Assoc = &str, Assoc2 = char>
|
||||
// size = 4, align = 4
|
||||
notable: u32
|
||||
```
|
||||
"#]],
|
||||
|
@ -7472,8 +7564,8 @@ impl Iterator for S {
|
|||
```
|
||||
|
||||
```rust
|
||||
// Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S>
|
||||
// size = 0, align = 1
|
||||
// Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S>
|
||||
// size = 0, align = 1
|
||||
struct S
|
||||
```
|
||||
"#]],
|
||||
|
@ -7532,7 +7624,7 @@ extern "C" {
|
|||
```
|
||||
|
||||
```rust
|
||||
// size = 0, align = 1
|
||||
// size = 0, align = 1
|
||||
type Ty
|
||||
```
|
||||
"#]],
|
||||
|
@ -7560,7 +7652,7 @@ fn main() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
// Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S>
|
||||
// Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S>
|
||||
S
|
||||
```"#]],
|
||||
);
|
||||
|
|
|
@ -74,6 +74,10 @@ pub(super) fn hints(
|
|||
Ok(s) => s.value.text_range(),
|
||||
Err(_) => continue,
|
||||
},
|
||||
MirSpan::SelfParam => match source_map.self_param_syntax() {
|
||||
Some(s) => s.value.text_range(),
|
||||
None => continue,
|
||||
},
|
||||
MirSpan::Unknown => continue,
|
||||
};
|
||||
let binding = &hir.bindings[*binding];
|
||||
|
|
|
@ -259,7 +259,7 @@ impl Analysis {
|
|||
false,
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
);
|
||||
change.change_file(file_id, Some(Arc::from(text)));
|
||||
change.change_file(file_id, Some(text));
|
||||
change.set_crate_graph(crate_graph);
|
||||
change.set_target_data_layouts(vec![Err("fixture has no layout".into())]);
|
||||
change.set_toolchains(vec![None]);
|
||||
|
|
|
@ -176,14 +176,12 @@ impl NavigationTarget {
|
|||
|
||||
impl TryToNav for FileSymbol {
|
||||
fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
|
||||
let root = db.parse_or_expand(self.loc.hir_file_id);
|
||||
self.loc.ptr.to_node(&root);
|
||||
Some(
|
||||
orig_range_with_focus(
|
||||
orig_range_with_focus_r(
|
||||
db,
|
||||
self.loc.hir_file_id,
|
||||
&self.loc.ptr.to_node(&root),
|
||||
Some(self.loc.name_ptr.to_node(&root)),
|
||||
self.loc.ptr.text_range(),
|
||||
Some(self.loc.name_ptr.text_range()),
|
||||
)
|
||||
.map(|(FileRange { file_id, range: full_range }, focus_range)| {
|
||||
NavigationTarget {
|
||||
|
@ -722,7 +720,21 @@ fn orig_range_with_focus(
|
|||
value: &SyntaxNode,
|
||||
name: Option<impl AstNode>,
|
||||
) -> UpmappingResult<(FileRange, Option<TextRange>)> {
|
||||
let Some(name) = name else { return orig_range(db, hir_file, value) };
|
||||
orig_range_with_focus_r(
|
||||
db,
|
||||
hir_file,
|
||||
value.text_range(),
|
||||
name.map(|it| it.syntax().text_range()),
|
||||
)
|
||||
}
|
||||
|
||||
fn orig_range_with_focus_r(
|
||||
db: &RootDatabase,
|
||||
hir_file: HirFileId,
|
||||
value: TextRange,
|
||||
name: Option<TextRange>,
|
||||
) -> UpmappingResult<(FileRange, Option<TextRange>)> {
|
||||
let Some(name) = name else { return orig_range_r(db, hir_file, value) };
|
||||
|
||||
let call_kind =
|
||||
|| db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id).kind;
|
||||
|
@ -733,9 +745,9 @@ fn orig_range_with_focus(
|
|||
.definition_range(db)
|
||||
};
|
||||
|
||||
let value_range = InFile::new(hir_file, value).original_file_range_opt(db);
|
||||
let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db);
|
||||
let ((call_site_range, call_site_focus), def_site) =
|
||||
match InFile::new(hir_file, name.syntax()).original_file_range_opt(db) {
|
||||
match InFile::new(hir_file, name).original_node_file_range_opt(db) {
|
||||
// call site name
|
||||
Some((focus_range, ctxt)) if ctxt.is_root() => {
|
||||
// Try to upmap the node as well, if it ends up in the def site, go back to the call site
|
||||
|
@ -802,7 +814,7 @@ fn orig_range_with_focus(
|
|||
}
|
||||
}
|
||||
// lost name? can't happen for single tokens
|
||||
None => return orig_range(db, hir_file, value),
|
||||
None => return orig_range_r(db, hir_file, value),
|
||||
};
|
||||
|
||||
UpmappingResult {
|
||||
|
@ -840,7 +852,18 @@ fn orig_range(
|
|||
value: &SyntaxNode,
|
||||
) -> UpmappingResult<(FileRange, Option<TextRange>)> {
|
||||
UpmappingResult {
|
||||
call_site: (InFile::new(hir_file, value).original_file_range(db), None),
|
||||
call_site: (InFile::new(hir_file, value).original_file_range_rooted(db), None),
|
||||
def_site: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn orig_range_r(
|
||||
db: &RootDatabase,
|
||||
hir_file: HirFileId,
|
||||
value: TextRange,
|
||||
) -> UpmappingResult<(FileRange, Option<TextRange>)> {
|
||||
UpmappingResult {
|
||||
call_site: (InFile::new(hir_file, value).original_node_file_range(db).0, None),
|
||||
def_site: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1710,7 +1710,7 @@ use proc_macros::mirror;
|
|||
mirror$0! {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
mirror Macro FileId(1) 1..77 22..28
|
||||
mirror ProcMacro FileId(1) 1..77 22..28
|
||||
|
||||
FileId(0) 26..32
|
||||
"#]],
|
||||
|
|
|
@ -138,7 +138,9 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
|||
}) {
|
||||
if let Some(def) = def {
|
||||
let file_id = match def {
|
||||
Definition::Module(it) => it.declaration_source(db).map(|src| src.file_id),
|
||||
Definition::Module(it) => {
|
||||
it.declaration_source_range(db).map(|src| src.file_id)
|
||||
}
|
||||
Definition::Function(it) => it.source(db).map(|src| src.file_id),
|
||||
_ => None,
|
||||
};
|
||||
|
@ -269,15 +271,10 @@ fn find_related_tests_in_module(
|
|||
Some(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
let mod_source = parent_module.definition_source(sema.db);
|
||||
let range = match &mod_source.value {
|
||||
hir::ModuleSource::Module(m) => m.syntax().text_range(),
|
||||
hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(),
|
||||
hir::ModuleSource::SourceFile(f) => f.syntax().text_range(),
|
||||
};
|
||||
let mod_source = parent_module.definition_source_range(sema.db);
|
||||
|
||||
let file_id = mod_source.file_id.original_file(sema.db);
|
||||
let mod_scope = SearchScope::file_range(FileRange { file_id, range });
|
||||
let mod_scope = SearchScope::file_range(FileRange { file_id, range: mod_source.value });
|
||||
let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() };
|
||||
find_related_tests(sema, syntax, fn_pos, Some(mod_scope), tests)
|
||||
}
|
||||
|
@ -405,14 +402,15 @@ fn runnable_mod_outline_definition(
|
|||
|
||||
let attrs = def.attrs(sema.db);
|
||||
let cfg = attrs.cfg();
|
||||
match def.definition_source(sema.db).value {
|
||||
hir::ModuleSource::SourceFile(_) => Some(Runnable {
|
||||
if def.as_source_file_id(sema.db).is_some() {
|
||||
Some(Runnable {
|
||||
use_name_in_title: false,
|
||||
nav: def.to_nav(sema.db).call_site(),
|
||||
kind: RunnableKind::TestMod { path },
|
||||
cfg,
|
||||
}),
|
||||
_ => None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -248,6 +248,7 @@ fn traverse(
|
|||
// an attribute nested in a macro call will not emit `inside_attribute`
|
||||
let mut inside_attribute = false;
|
||||
let mut inside_macro_call = false;
|
||||
let mut inside_proc_macro_call = false;
|
||||
|
||||
// Walk all nodes, keeping track of whether we are inside a macro or not.
|
||||
// If in macro, expand it first and highlight the expanded code.
|
||||
|
@ -298,8 +299,9 @@ fn traverse(
|
|||
ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => {
|
||||
bindings_shadow_count.clear()
|
||||
}
|
||||
ast::Item::MacroCall(_) => {
|
||||
ast::Item::MacroCall(ref macro_call) => {
|
||||
inside_macro_call = true;
|
||||
inside_proc_macro_call = sema.is_proc_macro_call(macro_call);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
@ -344,6 +346,7 @@ fn traverse(
|
|||
}
|
||||
Some(ast::Item::MacroCall(_)) => {
|
||||
inside_macro_call = false;
|
||||
inside_proc_macro_call = false;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
@ -519,6 +522,9 @@ fn traverse(
|
|||
highlight |= HlMod::Attribute
|
||||
}
|
||||
if inside_macro_call && tt_level > 0 {
|
||||
if inside_proc_macro_call {
|
||||
highlight |= HlMod::ProcMacro
|
||||
}
|
||||
highlight |= HlMod::Macro
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
.numeric_literal { color: #BFEBBF; }
|
||||
.bool_literal { color: #BFE6EB; }
|
||||
.macro { color: #94BFF3; }
|
||||
.proc_macro { color: #94BFF3; text-decoration: underline; }
|
||||
.derive { color: #94BFF3; font-style: italic; }
|
||||
.module { color: #AFD8AF; }
|
||||
.value_param { color: #DCDCCC; }
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue