Auto merge of #134681 - lnicola:sync-from-ra, r=lnicola

Subtree update of `rust-analyzer`

r? `@ghost`
This commit is contained in:
bors 2024-12-23 12:18:14 +00:00
commit 485f5e80e6
113 changed files with 3098 additions and 2167 deletions

83
Cargo.lock generated
View file

@ -1008,18 +1008,6 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lsp-server"
version = "0.7.7"
dependencies = [
"crossbeam-channel",
"ctrlc",
"log",
"lsp-types",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "lsp-server" name = "lsp-server"
version = "0.7.7" version = "0.7.7"
@ -1032,6 +1020,19 @@ dependencies = [
"serde_json", "serde_json",
] ]
[[package]]
name = "lsp-server"
version = "0.7.8"
dependencies = [
"crossbeam-channel",
"ctrlc",
"log",
"lsp-types",
"serde",
"serde_derive",
"serde_json",
]
[[package]] [[package]]
name = "lsp-types" name = "lsp-types"
version = "0.95.0" version = "0.95.0"
@ -1289,7 +1290,6 @@ name = "paths"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"camino", "camino",
"serde",
] ]
[[package]] [[package]]
@ -1352,12 +1352,12 @@ dependencies = [
name = "proc-macro-api" name = "proc-macro-api"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"base-db",
"indexmap", "indexmap",
"intern", "intern",
"paths", "paths",
"rustc-hash 2.0.0", "rustc-hash 2.0.0",
"serde", "serde",
"serde_derive",
"serde_json", "serde_json",
"span", "span",
"stdx", "stdx",
@ -1369,7 +1369,6 @@ dependencies = [
name = "proc-macro-srv" name = "proc-macro-srv"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"base-db",
"expect-test", "expect-test",
"intern", "intern",
"libloading", "libloading",
@ -1448,6 +1447,7 @@ dependencies = [
"rustc-hash 2.0.0", "rustc-hash 2.0.0",
"semver", "semver",
"serde", "serde",
"serde_derive",
"serde_json", "serde_json",
"span", "span",
"stdx", "stdx",
@ -1507,9 +1507,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_abi" name = "ra-ap-rustc_abi"
version = "0.85.0" version = "0.87.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af462c3a2d524b84a51b6848b439787f01b35c6c1086d3e3086a5f5eea92ed9a" checksum = "28b782af0a7a8df16ddf43cd70da9f17bc3b1ce712c9e4992b6edb16f5f53632"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"ra-ap-rustc_index", "ra-ap-rustc_index",
@ -1518,9 +1518,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_index" name = "ra-ap-rustc_index"
version = "0.85.0" version = "0.87.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be6bb8cb0ab78d94a222f1ffd3e87254cdfb57413382b8d6ebe26a85482f99d1" checksum = "ce5742f134960482f543b35ecebec3cacc6d79a9a685713518b4d8d70c5f9aa8"
dependencies = [ dependencies = [
"ra-ap-rustc_index_macros", "ra-ap-rustc_index_macros",
"smallvec", "smallvec",
@ -1528,9 +1528,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_index_macros" name = "ra-ap-rustc_index_macros"
version = "0.85.0" version = "0.87.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c24b1641455b46e87435b7321219672077066e678963d239a4a2904732979b16" checksum = "d7ea011fcf68309a8835ad01d91c032cb18444617b00e2cab21d45b208164441"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1539,9 +1539,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_lexer" name = "ra-ap-rustc_lexer"
version = "0.85.0" version = "0.87.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94daa86974417981fed2f12bd8fb00158dfa6fee561152bed689278c846d0272" checksum = "eb76f0a4d4c20859e41f0a23bff0f37ab9ca9171c214a6c7dd72ea69434865dc"
dependencies = [ dependencies = [
"unicode-properties", "unicode-properties",
"unicode-xid", "unicode-xid",
@ -1549,9 +1549,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_parse_format" name = "ra-ap-rustc_parse_format"
version = "0.85.0" version = "0.87.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc07f6bd581746f358e39c4b6bfe8d455b3d6ad1a857821016d0d42eeb5e1e3e" checksum = "06080bd35078305421a62da77f3c128482d8d44441b6da8ce9d146d1cd9cdb5b"
dependencies = [ dependencies = [
"ra-ap-rustc_index", "ra-ap-rustc_index",
"ra-ap-rustc_lexer", "ra-ap-rustc_lexer",
@ -1559,9 +1559,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_pattern_analysis" name = "ra-ap-rustc_pattern_analysis"
version = "0.85.0" version = "0.87.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f49b86e1276c1c3c72898410def29b699415f4e7d1dfb3531daf79794694372" checksum = "68a3154fe4c20c177d7b3c678a2d3a97aba0cca156ddef88959915041889daf0"
dependencies = [ dependencies = [
"ra-ap-rustc_index", "ra-ap-rustc_index",
"rustc-hash 2.0.0", "rustc-hash 2.0.0",
@ -1676,7 +1676,7 @@ dependencies = [
"intern", "intern",
"itertools", "itertools",
"load-cargo", "load-cargo",
"lsp-server 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-server 0.7.7",
"lsp-types", "lsp-types",
"memchr", "memchr",
"mimalloc", "mimalloc",
@ -1695,6 +1695,7 @@ dependencies = [
"scip", "scip",
"semver", "semver",
"serde", "serde",
"serde_derive",
"serde_json", "serde_json",
"stdx", "stdx",
"syntax", "syntax",
@ -1822,18 +1823,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.206" version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.206" version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1924,12 +1925,6 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]] [[package]]
name = "stdx" name = "stdx"
version = "0.0.0" version = "0.0.0"
@ -1946,9 +1941,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.74" version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2264,13 +2259,9 @@ dependencies = [
[[package]] [[package]]
name = "triomphe" name = "triomphe"
version = "0.1.13" version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85"
dependencies = [
"serde",
"stable_deref_trait",
]
[[package]] [[package]]
name = "tt" name = "tt"

View file

@ -85,11 +85,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" }
edition = { path = "./crates/edition", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" }
ra-ap-rustc_lexer = { version = "0.85", default-features = false } ra-ap-rustc_lexer = { version = "0.87", default-features = false }
ra-ap-rustc_parse_format = { version = "0.85", default-features = false } ra-ap-rustc_parse_format = { version = "0.87", default-features = false }
ra-ap-rustc_index = { version = "0.85", default-features = false } ra-ap-rustc_index = { version = "0.87", default-features = false }
ra-ap-rustc_abi = { version = "0.85", default-features = false } ra-ap-rustc_abi = { version = "0.87", default-features = false }
ra-ap-rustc_pattern_analysis = { version = "0.85", default-features = false } ra-ap-rustc_pattern_analysis = { version = "0.87", default-features = false }
# local crates that aren't published to crates.io. These should not have versions. # local crates that aren't published to crates.io. These should not have versions.
test-fixture = { path = "./crates/test-fixture" } test-fixture = { path = "./crates/test-fixture" }
@ -138,7 +138,8 @@ pulldown-cmark = { version = "0.9.0", default-features = false }
rayon = "1.8.0" rayon = "1.8.0"
rustc-hash = "2.0.0" rustc-hash = "2.0.0"
semver = "1.0.14" semver = "1.0.14"
serde = { version = "1.0.192", features = ["derive"] } serde = { version = "1.0.192" }
serde_derive = { version = "1.0.192" }
serde_json = "1.0.108" serde_json = "1.0.108"
smallvec = { version = "1.10.0", features = [ smallvec = { version = "1.10.0", features = [
"const_new", "const_new",
@ -157,7 +158,7 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features =
"time", "time",
"tracing-log", "tracing-log",
] } ] }
triomphe = { version = "0.1.10", default-features = false, features = ["std"] } triomphe = { version = "0.1.14", default-features = false, features = ["std"] }
url = "2.3.1" url = "2.3.1"
xshell = "0.2.5" xshell = "0.2.5"

View file

@ -18,6 +18,7 @@ use smallvec::SmallVec;
use span::{Edition, MacroFileId}; use span::{Edition, MacroFileId};
use syntax::{ast, AstPtr, SyntaxNodePtr}; use syntax::{ast, AstPtr, SyntaxNodePtr};
use triomphe::Arc; use triomphe::Arc;
use tt::TextRange;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
@ -143,15 +144,7 @@ pub struct BodySourceMap {
pub types: TypesSourceMap, pub types: TypesSourceMap,
// FIXME: Make this a sane struct. template_map: Option<Box<FormatTemplate>>,
template_map: Option<
Box<(
// format_args!
FxHashMap<ExprId, (HygieneId, Vec<(syntax::TextRange, Name)>)>,
// asm!
FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
)>,
>,
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>, expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>,
@ -160,6 +153,20 @@ pub struct BodySourceMap {
diagnostics: Vec<BodyDiagnostic>, diagnostics: Vec<BodyDiagnostic>,
} }
#[derive(Default, Debug, Eq, PartialEq)]
struct FormatTemplate {
/// A map from `format_args!()` expressions to their captures.
format_args_to_captures: FxHashMap<ExprId, (HygieneId, Vec<(syntax::TextRange, Name)>)>,
/// A map from `asm!()` expressions to their captures.
asm_to_captures: FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
/// A map from desugared expressions of implicit captures to their source.
///
/// The value stored for each capture is its template literal and offset inside it. The template literal
/// is from the `format_args[_nl]!()` macro and so needs to be mapped up once to go to the user-written
/// template.
implicit_capture_to_source: FxHashMap<ExprId, InFile<(AstPtr<ast::Expr>, TextRange)>>,
}
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub enum BodyDiagnostic { pub enum BodyDiagnostic {
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions }, InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
@ -798,18 +805,29 @@ impl BodySourceMap {
node: InFile<&ast::FormatArgsExpr>, node: InFile<&ast::FormatArgsExpr>,
) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> { ) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>); let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
let (hygiene, names) = let (hygiene, names) = self
self.template_map.as_ref()?.0.get(&self.expr_map.get(&src)?.as_expr()?)?; .template_map
.as_ref()?
.format_args_to_captures
.get(&self.expr_map.get(&src)?.as_expr()?)?;
Some((*hygiene, &**names)) Some((*hygiene, &**names))
} }
pub fn format_args_implicit_capture(
&self,
capture_expr: ExprId,
) -> Option<InFile<(AstPtr<ast::Expr>, TextRange)>> {
self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied()
}
pub fn asm_template_args( pub fn asm_template_args(
&self, &self,
node: InFile<&ast::AsmExpr>, node: InFile<&ast::AsmExpr>,
) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> { ) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>); let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
let expr = self.expr_map.get(&src)?.as_expr()?; let expr = self.expr_map.get(&src)?.as_expr()?;
Some(expr).zip(self.template_map.as_ref()?.1.get(&expr).map(std::ops::Deref::deref)) Some(expr)
.zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref))
} }
/// Get a reference to the body source map's diagnostics. /// Get a reference to the body source map's diagnostics.
@ -835,8 +853,14 @@ impl BodySourceMap {
types, types,
} = self; } = self;
if let Some(template_map) = template_map { if let Some(template_map) = template_map {
template_map.0.shrink_to_fit(); let FormatTemplate {
template_map.1.shrink_to_fit(); format_args_to_captures,
asm_to_captures,
implicit_capture_to_source,
} = &mut **template_map;
format_args_to_captures.shrink_to_fit();
asm_to_captures.shrink_to_fit();
implicit_capture_to_source.shrink_to_fit();
} }
expr_map.shrink_to_fit(); expr_map.shrink_to_fit();
expr_map_back.shrink_to_fit(); expr_map_back.shrink_to_fit();

View file

@ -1957,8 +1957,10 @@ impl ExprCollector<'_> {
_ => None, _ => None,
}); });
let mut mappings = vec![]; let mut mappings = vec![];
let (fmt, hygiene) = match template.and_then(|it| self.expand_macros_to_string(it)) { let (fmt, hygiene) = match template.and_then(|template| {
Some((s, is_direct_literal)) => { self.expand_macros_to_string(template.clone()).map(|it| (it, template))
}) {
Some(((s, is_direct_literal), template)) => {
let call_ctx = self.expander.syntax_context(); let call_ctx = self.expander.syntax_context();
let hygiene = self.hygiene_id_for(s.syntax().text_range().start()); let hygiene = self.hygiene_id_for(s.syntax().text_range().start());
let fmt = format_args::parse( let fmt = format_args::parse(
@ -1966,8 +1968,18 @@ impl ExprCollector<'_> {
fmt_snippet, fmt_snippet,
args, args,
is_direct_literal, is_direct_literal,
|name| { |name, range| {
let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name))); let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
if let Some(range) = range {
self.source_map
.template_map
.get_or_insert_with(Default::default)
.implicit_capture_to_source
.insert(
expr_id,
self.expander.in_file((AstPtr::new(&template), range)),
);
}
if !hygiene.is_root() { if !hygiene.is_root() {
self.body.expr_hygiene.insert(expr_id, hygiene); self.body.expr_hygiene.insert(expr_id, hygiene);
} }
@ -2139,7 +2151,7 @@ impl ExprCollector<'_> {
self.source_map self.source_map
.template_map .template_map
.get_or_insert_with(Default::default) .get_or_insert_with(Default::default)
.0 .format_args_to_captures
.insert(idx, (hygiene, mappings)); .insert(idx, (hygiene, mappings));
idx idx
} }

View file

@ -6,7 +6,7 @@ use syntax::{
ast::{self, HasName, IsString}, ast::{self, HasName, IsString},
AstNode, AstPtr, AstToken, T, AstNode, AstPtr, AstToken, T,
}; };
use tt::{TextRange, TextSize}; use tt::TextRange;
use crate::{ use crate::{
body::lower::{ExprCollector, FxIndexSet}, body::lower::{ExprCollector, FxIndexSet},
@ -224,7 +224,7 @@ impl ExprCollector<'_> {
TextRange::new( TextRange::new(
inner_span.start.try_into().unwrap(), inner_span.start.try_into().unwrap(),
inner_span.end.try_into().unwrap(), inner_span.end.try_into().unwrap(),
) - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1) )
}) })
}; };
for piece in unverified_pieces { for piece in unverified_pieces {
@ -268,7 +268,11 @@ impl ExprCollector<'_> {
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }), Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
syntax_ptr, syntax_ptr,
); );
self.source_map.template_map.get_or_insert_with(Default::default).1.insert(idx, mappings); self.source_map
.template_map
.get_or_insert_with(Default::default)
.asm_to_captures
.insert(idx, mappings);
idx idx
} }
} }

View file

@ -685,6 +685,7 @@ impl Printer<'_> {
self.print_binding(*id); self.print_binding(*id);
if let Some(pat) = subpat { if let Some(pat) = subpat {
self.whitespace(); self.whitespace();
w!(self, "@ ");
self.print_pat(*pat); self.print_pat(*pat);
} }
} }

View file

@ -426,3 +426,21 @@ fn f() {
"should have a binding for `B`", "should have a binding for `B`",
); );
} }
#[test]
fn regression_pretty_print_bind_pat() {
let (db, body, owner) = lower(
r#"
fn foo() {
let v @ u = 123;
}
"#,
);
let printed = body.pretty_print(&db, owner, Edition::CURRENT);
assert_eq!(
printed,
r#"fn foo() -> () {
let v @ u = 123;
}"#
);
}

View file

@ -1,5 +1,6 @@
//! Parses `format_args` input. //! Parses `format_args` input.
use either::Either;
use hir_expand::name::Name; use hir_expand::name::Name;
use intern::Symbol; use intern::Symbol;
use rustc_parse_format as parse; use rustc_parse_format as parse;
@ -7,7 +8,7 @@ use span::SyntaxContextId;
use stdx::TupleExt; use stdx::TupleExt;
use syntax::{ use syntax::{
ast::{self, IsString}, ast::{self, IsString},
TextRange, TextSize, TextRange,
}; };
use crate::hir::ExprId; use crate::hir::ExprId;
@ -33,7 +34,7 @@ pub enum FormatArgsPiece {
Placeholder(FormatPlaceholder), Placeholder(FormatPlaceholder),
} }
#[derive(Copy, Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct FormatPlaceholder { pub struct FormatPlaceholder {
/// Index into [`FormatArgs::arguments`]. /// Index into [`FormatArgs::arguments`].
pub argument: FormatArgPosition, pub argument: FormatArgPosition,
@ -45,11 +46,11 @@ pub struct FormatPlaceholder {
pub format_options: FormatOptions, pub format_options: FormatOptions,
} }
#[derive(Copy, Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct FormatArgPosition { pub struct FormatArgPosition {
/// Which argument this position refers to (Ok), /// Which argument this position refers to (Ok),
/// or would've referred to if it existed (Err). /// or would've referred to if it existed (Err).
pub index: Result<usize, usize>, pub index: Result<usize, Either<usize, Name>>,
/// What kind of position this is. See [`FormatArgPositionKind`]. /// What kind of position this is. See [`FormatArgPositionKind`].
pub kind: FormatArgPositionKind, pub kind: FormatArgPositionKind,
/// The span of the name or number. /// The span of the name or number.
@ -88,7 +89,7 @@ pub enum FormatTrait {
UpperHex, UpperHex,
} }
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] #[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct FormatOptions { pub struct FormatOptions {
/// The width. E.g. `{:5}` or `{:width$}`. /// The width. E.g. `{:5}` or `{:width$}`.
pub width: Option<FormatCount>, pub width: Option<FormatCount>,
@ -133,7 +134,7 @@ pub enum FormatAlignment {
Center, Center,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum FormatCount { pub enum FormatCount {
/// `{:5}` or `{:.5}` /// `{:5}` or `{:.5}`
Literal(usize), Literal(usize),
@ -173,7 +174,7 @@ pub(crate) fn parse(
fmt_snippet: Option<String>, fmt_snippet: Option<String>,
mut args: FormatArgumentsCollector, mut args: FormatArgumentsCollector,
is_direct_literal: bool, is_direct_literal: bool,
mut synth: impl FnMut(Name) -> ExprId, mut synth: impl FnMut(Name, Option<TextRange>) -> ExprId,
mut record_usage: impl FnMut(Name, Option<TextRange>), mut record_usage: impl FnMut(Name, Option<TextRange>),
call_ctx: SyntaxContextId, call_ctx: SyntaxContextId,
) -> FormatArgs { ) -> FormatArgs {
@ -192,7 +193,6 @@ pub(crate) fn parse(
} }
None => None, None => None,
}; };
let mut parser = let mut parser =
parse::Parser::new(&text, str_style, fmt_snippet, false, parse::ParseMode::Format); parse::Parser::new(&text, str_style, fmt_snippet, false, parse::ParseMode::Format);
@ -217,7 +217,6 @@ pub(crate) fn parse(
let to_span = |inner_span: parse::InnerSpan| { let to_span = |inner_span: parse::InnerSpan| {
is_source_literal.then(|| { is_source_literal.then(|| {
TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap()) TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
- TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1)
}) })
}; };
@ -245,8 +244,8 @@ pub(crate) fn parse(
Ok(index) Ok(index)
} else { } else {
// Doesn't exist as an explicit argument. // Doesn't exist as an explicit argument.
invalid_refs.push((index, span, used_as, kind)); invalid_refs.push((Either::Left(index), span, used_as, kind));
Err(index) Err(Either::Left(index))
} }
} }
ArgRef::Name(name, span) => { ArgRef::Name(name, span) => {
@ -265,14 +264,17 @@ pub(crate) fn parse(
// For the moment capturing variables from format strings expanded from macros is // For the moment capturing variables from format strings expanded from macros is
// disabled (see RFC #2795) // disabled (see RFC #2795)
// FIXME: Diagnose // FIXME: Diagnose
invalid_refs.push((Either::Right(name.clone()), span, used_as, kind));
Err(Either::Right(name))
} else {
record_usage(name.clone(), span);
Ok(args.add(FormatArgument {
kind: FormatArgumentKind::Captured(name.clone()),
// FIXME: This is problematic, we might want to synthesize a dummy
// expression proper and/or desugar these.
expr: synth(name, span),
}))
} }
record_usage(name.clone(), span);
Ok(args.add(FormatArgument {
kind: FormatArgumentKind::Captured(name.clone()),
// FIXME: This is problematic, we might want to synthesize a dummy
// expression proper and/or desugar these.
expr: synth(name),
}))
} }
} }
}; };

View file

@ -16,7 +16,7 @@ use syntax::ast;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
per_ns::PerNs, per_ns::{Item, MacrosItem, PerNs, TypesItem, ValuesItem},
visibility::{Visibility, VisibilityExplicitness}, visibility::{Visibility, VisibilityExplicitness},
AdtId, BuiltinType, ConstId, ExternCrateId, FxIndexMap, HasModule, ImplId, LocalModuleId, AdtId, BuiltinType, ConstId, ExternCrateId, FxIndexMap, HasModule, ImplId, LocalModuleId,
Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
@ -80,9 +80,9 @@ pub struct ItemScope {
/// Defs visible in this scope. This includes `declarations`, but also /// Defs visible in this scope. This includes `declarations`, but also
/// imports. The imports belong to this module and can be resolved by using them on /// imports. The imports belong to this module and can be resolved by using them on
/// the `use_imports_*` fields. /// the `use_imports_*` fields.
types: FxIndexMap<Name, (ModuleDefId, Visibility, Option<ImportOrExternCrate>)>, types: FxIndexMap<Name, TypesItem>,
values: FxIndexMap<Name, (ModuleDefId, Visibility, Option<ImportId>)>, values: FxIndexMap<Name, ValuesItem>,
macros: FxIndexMap<Name, (MacroId, Visibility, Option<ImportId>)>, macros: FxIndexMap<Name, MacrosItem>,
unresolved: FxHashSet<Name>, unresolved: FxHashSet<Name>,
/// The defs declared in this scope. Each def has a single scope where it is /// The defs declared in this scope. Each def has a single scope where it is
@ -92,7 +92,7 @@ pub struct ItemScope {
impls: Vec<ImplId>, impls: Vec<ImplId>,
unnamed_consts: Vec<ConstId>, unnamed_consts: Vec<ConstId>,
/// Traits imported via `use Trait as _;`. /// Traits imported via `use Trait as _;`.
unnamed_trait_imports: FxHashMap<TraitId, (Visibility, Option<ImportId>)>, unnamed_trait_imports: FxHashMap<TraitId, Item<()>>,
// the resolutions of the imports of this scope // the resolutions of the imports of this scope
use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>, use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>,
@ -187,7 +187,7 @@ impl ItemScope {
import = i; import = i;
} }
ImportOrDef::Def(ModuleDefId::MacroId(def)) => { ImportOrDef::Def(ModuleDefId::MacroId(def)) => {
res.macros = Some((def, Visibility::Public, None)); res.macros = Some(Item { def, vis: Visibility::Public, import: None });
break; break;
} }
_ => break, _ => break,
@ -203,7 +203,7 @@ impl ItemScope {
import = i; import = i;
} }
ImportOrDef::Def(def) => { ImportOrDef::Def(def) => {
res.types = Some((def, Visibility::Public, None)); res.types = Some(Item { def, vis: Visibility::Public, import: None });
break; break;
} }
_ => break, _ => break,
@ -219,7 +219,7 @@ impl ItemScope {
import = i; import = i;
} }
ImportOrDef::Def(def) => { ImportOrDef::Def(def) => {
res.values = Some((def, Visibility::Public, None)); res.values = Some(Item { def, vis: Visibility::Public, import: None });
break; break;
} }
_ => break, _ => break,
@ -253,8 +253,8 @@ impl ItemScope {
} }
pub(crate) fn modules_in_scope(&self) -> impl Iterator<Item = (ModuleId, Visibility)> + '_ { pub(crate) fn modules_in_scope(&self) -> impl Iterator<Item = (ModuleId, Visibility)> + '_ {
self.types.values().copied().filter_map(|(def, vis, _)| match def { self.types.values().filter_map(|ns| match ns.def {
ModuleDefId::ModuleId(module) => Some((module, vis)), ModuleDefId::ModuleId(module) => Some((module, ns.vis)),
_ => None, _ => None,
}) })
} }
@ -283,20 +283,20 @@ impl ItemScope {
} }
pub(crate) fn type_(&self, name: &Name) -> Option<(ModuleDefId, Visibility)> { pub(crate) fn type_(&self, name: &Name) -> Option<(ModuleDefId, Visibility)> {
self.types.get(name).copied().map(|(a, b, _)| (a, b)) self.types.get(name).map(|item| (item.def, item.vis))
} }
/// XXX: this is O(N) rather than O(1), try to not introduce new usages. /// XXX: this is O(N) rather than O(1), try to not introduce new usages.
pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility, /*declared*/ bool)> { pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility, /*declared*/ bool)> {
match item { match item {
ItemInNs::Macros(def) => self.macros.iter().find_map(|(name, &(other_def, vis, i))| { ItemInNs::Macros(def) => self.macros.iter().find_map(|(name, other_def)| {
(other_def == def).then_some((name, vis, i.is_none())) (other_def.def == def).then_some((name, other_def.vis, other_def.import.is_none()))
}), }),
ItemInNs::Types(def) => self.types.iter().find_map(|(name, &(other_def, vis, i))| { ItemInNs::Types(def) => self.types.iter().find_map(|(name, other_def)| {
(other_def == def).then_some((name, vis, i.is_none())) (other_def.def == def).then_some((name, other_def.vis, other_def.import.is_none()))
}), }),
ItemInNs::Values(def) => self.values.iter().find_map(|(name, &(other_def, vis, i))| { ItemInNs::Values(def) => self.values.iter().find_map(|(name, other_def)| {
(other_def == def).then_some((name, vis, i.is_none())) (other_def.def == def).then_some((name, other_def.vis, other_def.import.is_none()))
}), }),
} }
} }
@ -311,22 +311,34 @@ impl ItemScope {
ItemInNs::Macros(def) => self ItemInNs::Macros(def) => self
.macros .macros
.iter() .iter()
.filter_map(|(name, &(other_def, vis, i))| { .filter_map(|(name, other_def)| {
(other_def == def).then_some((name, vis, i.is_none())) (other_def.def == def).then_some((
name,
other_def.vis,
other_def.import.is_none(),
))
}) })
.find_map(|(a, b, c)| cb(a, b, c)), .find_map(|(a, b, c)| cb(a, b, c)),
ItemInNs::Types(def) => self ItemInNs::Types(def) => self
.types .types
.iter() .iter()
.filter_map(|(name, &(other_def, vis, i))| { .filter_map(|(name, other_def)| {
(other_def == def).then_some((name, vis, i.is_none())) (other_def.def == def).then_some((
name,
other_def.vis,
other_def.import.is_none(),
))
}) })
.find_map(|(a, b, c)| cb(a, b, c)), .find_map(|(a, b, c)| cb(a, b, c)),
ItemInNs::Values(def) => self ItemInNs::Values(def) => self
.values .values
.iter() .iter()
.filter_map(|(name, &(other_def, vis, i))| { .filter_map(|(name, other_def)| {
(other_def == def).then_some((name, vis, i.is_none())) (other_def.def == def).then_some((
name,
other_def.vis,
other_def.import.is_none(),
))
}) })
.find_map(|(a, b, c)| cb(a, b, c)), .find_map(|(a, b, c)| cb(a, b, c)),
} }
@ -335,7 +347,7 @@ impl ItemScope {
pub(crate) fn traits(&self) -> impl Iterator<Item = TraitId> + '_ { pub(crate) fn traits(&self) -> impl Iterator<Item = TraitId> + '_ {
self.types self.types
.values() .values()
.filter_map(|&(def, _, _)| match def { .filter_map(|def| match def.def {
ModuleDefId::TraitId(t) => Some(t), ModuleDefId::TraitId(t) => Some(t),
_ => None, _ => None,
}) })
@ -344,13 +356,13 @@ impl ItemScope {
pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ { pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ {
self.entries().map(|(name, res)| (Some(name.clone()), res)).chain( self.entries().map(|(name, res)| (Some(name.clone()), res)).chain(
self.unnamed_trait_imports.iter().map(|(tr, (vis, i))| { self.unnamed_trait_imports.iter().map(|(tr, trait_)| {
( (
None, None,
PerNs::types( PerNs::types(
ModuleDefId::TraitId(*tr), ModuleDefId::TraitId(*tr),
*vis, trait_.vis,
i.map(ImportOrExternCrate::Import), trait_.import.map(ImportOrExternCrate::Import),
), ),
) )
}), }),
@ -464,12 +476,12 @@ impl ItemScope {
// FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope // FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> { pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
self.unnamed_trait_imports.get(&tr).copied().map(|(a, _)| a) self.unnamed_trait_imports.get(&tr).map(|trait_| trait_.vis)
} }
pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) { pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) {
// FIXME: import // FIXME: import
self.unnamed_trait_imports.insert(tr, (vis, None)); self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import: None });
} }
pub(crate) fn push_res_with_import( pub(crate) fn push_res_with_import(
@ -502,7 +514,7 @@ impl ItemScope {
} }
None | Some(ImportType::Glob(_)) => None, None | Some(ImportType::Glob(_)) => None,
}; };
let prev = std::mem::replace(&mut fld.2, import); let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import { if let Some(import) = import {
self.use_imports_types.insert( self.use_imports_types.insert(
import, import,
@ -513,7 +525,7 @@ impl ItemScope {
Some(ImportOrExternCrate::ExternCrate(import)) => { Some(ImportOrExternCrate::ExternCrate(import)) => {
ImportOrDef::ExternCrate(import) ImportOrDef::ExternCrate(import)
} }
None => ImportOrDef::Def(fld.0), None => ImportOrDef::Def(fld.def),
}, },
); );
} }
@ -540,7 +552,7 @@ impl ItemScope {
} }
None | Some(ImportType::Glob(_)) => None, None | Some(ImportType::Glob(_)) => None,
}; };
let prev = std::mem::replace(&mut fld.2, import); let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import { if let Some(import) = import {
self.use_imports_types.insert( self.use_imports_types.insert(
import, import,
@ -551,7 +563,7 @@ impl ItemScope {
Some(ImportOrExternCrate::ExternCrate(import)) => { Some(ImportOrExternCrate::ExternCrate(import)) => {
ImportOrDef::ExternCrate(import) ImportOrDef::ExternCrate(import)
} }
None => ImportOrDef::Def(fld.0), None => ImportOrDef::Def(fld.def),
}, },
); );
} }
@ -579,13 +591,13 @@ impl ItemScope {
Some(ImportType::Import(import)) => Some(import), Some(ImportType::Import(import)) => Some(import),
_ => None, _ => None,
}; };
let prev = std::mem::replace(&mut fld.2, import); let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import { if let Some(import) = import {
self.use_imports_values.insert( self.use_imports_values.insert(
import, import,
match prev { match prev {
Some(import) => ImportOrDef::Import(import), Some(import) => ImportOrDef::Import(import),
None => ImportOrDef::Def(fld.0), None => ImportOrDef::Def(fld.def),
}, },
); );
} }
@ -599,13 +611,13 @@ impl ItemScope {
Some(ImportType::Import(import)) => Some(import), Some(ImportType::Import(import)) => Some(import),
_ => None, _ => None,
}; };
let prev = std::mem::replace(&mut fld.2, import); let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import { if let Some(import) = import {
self.use_imports_values.insert( self.use_imports_values.insert(
import, import,
match prev { match prev {
Some(import) => ImportOrDef::Import(import), Some(import) => ImportOrDef::Import(import),
None => ImportOrDef::Def(fld.0), None => ImportOrDef::Def(fld.def),
}, },
); );
} }
@ -631,13 +643,13 @@ impl ItemScope {
Some(ImportType::Import(import)) => Some(import), Some(ImportType::Import(import)) => Some(import),
_ => None, _ => None,
}; };
let prev = std::mem::replace(&mut fld.2, import); let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import { if let Some(import) = import {
self.use_imports_macros.insert( self.use_imports_macros.insert(
import, import,
match prev { match prev {
Some(import) => ImportOrDef::Import(import), Some(import) => ImportOrDef::Import(import),
None => ImportOrDef::Def(fld.0.into()), None => ImportOrDef::Def(fld.def.into()),
}, },
); );
} }
@ -651,13 +663,13 @@ impl ItemScope {
Some(ImportType::Import(import)) => Some(import), Some(ImportType::Import(import)) => Some(import),
_ => None, _ => None,
}; };
let prev = std::mem::replace(&mut fld.2, import); let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import { if let Some(import) = import {
self.use_imports_macros.insert( self.use_imports_macros.insert(
import, import,
match prev { match prev {
Some(import) => ImportOrDef::Import(import), Some(import) => ImportOrDef::Import(import),
None => ImportOrDef::Def(fld.0.into()), None => ImportOrDef::Def(fld.def.into()),
}, },
); );
} }
@ -680,19 +692,19 @@ impl ItemScope {
pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) { pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
self.types self.types
.values_mut() .values_mut()
.map(|(_, vis, _)| vis) .map(|def| &mut def.vis)
.chain(self.values.values_mut().map(|(_, vis, _)| vis)) .chain(self.values.values_mut().map(|def| &mut def.vis))
.chain(self.unnamed_trait_imports.values_mut().map(|(vis, _)| vis)) .chain(self.unnamed_trait_imports.values_mut().map(|def| &mut def.vis))
.for_each(|vis| { .for_each(|vis| {
*vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit)
}); });
for (mac, vis, import) in self.macros.values_mut() { for mac in self.macros.values_mut() {
if matches!(mac, MacroId::ProcMacroId(_) if import.is_none()) { if matches!(mac.def, MacroId::ProcMacroId(_) if mac.import.is_none()) {
continue; continue;
} }
*vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit); mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit);
} }
} }
@ -707,23 +719,23 @@ impl ItemScope {
name.map_or("_".to_owned(), |name| name.display(db, Edition::LATEST).to_string()) name.map_or("_".to_owned(), |name| name.display(db, Edition::LATEST).to_string())
); );
if let Some((.., i)) = def.types { if let Some(Item { import, .. }) = def.types {
buf.push_str(" t"); buf.push_str(" t");
match i { match import {
Some(ImportOrExternCrate::Import(_)) => buf.push('i'), Some(ImportOrExternCrate::Import(_)) => buf.push('i'),
Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'), Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'),
None => (), None => (),
} }
} }
if let Some((.., i)) = def.values { if let Some(Item { import, .. }) = def.values {
buf.push_str(" v"); buf.push_str(" v");
if i.is_some() { if import.is_some() {
buf.push('i'); buf.push('i');
} }
} }
if let Some((.., i)) = def.macros { if let Some(Item { import, .. }) = def.macros {
buf.push_str(" m"); buf.push_str(" m");
if i.is_some() { if import.is_some() {
buf.push('i'); buf.push('i');
} }
} }
@ -781,19 +793,19 @@ impl ItemScope {
pub(crate) fn update_visibility_types(&mut self, name: &Name, vis: Visibility) { pub(crate) fn update_visibility_types(&mut self, name: &Name, vis: Visibility) {
let res = let res =
self.types.get_mut(name).expect("tried to update visibility of non-existent type"); self.types.get_mut(name).expect("tried to update visibility of non-existent type");
res.1 = vis; res.vis = vis;
} }
pub(crate) fn update_visibility_values(&mut self, name: &Name, vis: Visibility) { pub(crate) fn update_visibility_values(&mut self, name: &Name, vis: Visibility) {
let res = let res =
self.values.get_mut(name).expect("tried to update visibility of non-existent value"); self.values.get_mut(name).expect("tried to update visibility of non-existent value");
res.1 = vis; res.vis = vis;
} }
pub(crate) fn update_visibility_macros(&mut self, name: &Name, vis: Visibility) { pub(crate) fn update_visibility_macros(&mut self, name: &Name, vis: Visibility) {
let res = let res =
self.macros.get_mut(name).expect("tried to update visibility of non-existent macro"); self.macros.get_mut(name).expect("tried to update visibility of non-existent macro");
res.1 = vis; res.vis = vis;
} }
} }

View file

@ -44,7 +44,7 @@ use crate::{
ResolveMode, ResolveMode,
}, },
path::{ImportAlias, ModPath, PathKind}, path::{ImportAlias, ModPath, PathKind},
per_ns::PerNs, per_ns::{Item, PerNs},
tt, tt,
visibility::{RawVisibility, Visibility}, visibility::{RawVisibility, Visibility},
AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantLoc, AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantLoc,
@ -523,7 +523,7 @@ impl DefCollector<'_> {
self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None); self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
match per_ns.types { match per_ns.types {
Some((ModuleDefId::ModuleId(m), _, import)) => { Some(Item { def: ModuleDefId::ModuleId(m), import, .. }) => {
// FIXME: This should specifically look for a glob import somehow and record that here // FIXME: This should specifically look for a glob import somehow and record that here
self.def_map.prelude = Some(( self.def_map.prelude = Some((
m, m,
@ -1069,9 +1069,9 @@ impl DefCollector<'_> {
// //
// This has been historically allowed, but may be not allowed in future // This has been historically allowed, but may be not allowed in future
// https://github.com/rust-lang/rust/issues/127909 // https://github.com/rust-lang/rust/issues/127909
if let Some((_, v, it)) = defs.types.as_mut() { if let Some(def) = defs.types.as_mut() {
let is_extern_crate_reimport_without_prefix = || { let is_extern_crate_reimport_without_prefix = || {
let Some(ImportOrExternCrate::ExternCrate(_)) = it else { let Some(ImportOrExternCrate::ExternCrate(_)) = def.import else {
return false; return false;
}; };
let Some(ImportType::Import(id)) = def_import_type else { let Some(ImportType::Import(id)) = def_import_type else {
@ -1086,16 +1086,16 @@ impl DefCollector<'_> {
path.segments().len() < 2 path.segments().len() < 2
}; };
if is_extern_crate_reimport_without_prefix() { if is_extern_crate_reimport_without_prefix() {
*v = vis; def.vis = vis;
} else { } else {
*v = v.min(vis, &self.def_map).unwrap_or(vis); def.vis = def.vis.min(vis, &self.def_map).unwrap_or(vis);
} }
} }
if let Some((_, v, _)) = defs.values.as_mut() { if let Some(def) = defs.values.as_mut() {
*v = v.min(vis, &self.def_map).unwrap_or(vis); def.vis = def.vis.min(vis, &self.def_map).unwrap_or(vis);
} }
if let Some((_, v, _)) = defs.macros.as_mut() { if let Some(def) = defs.macros.as_mut() {
*v = v.min(vis, &self.def_map).unwrap_or(vis); def.vis = def.vis.min(vis, &self.def_map).unwrap_or(vis);
} }
let mut changed = false; let mut changed = false;
@ -1106,12 +1106,12 @@ impl DefCollector<'_> {
// Multiple globs may import the same item and they may override visibility from // Multiple globs may import the same item and they may override visibility from
// previously resolved globs. Handle overrides here and leave the rest to // previously resolved globs. Handle overrides here and leave the rest to
// `ItemScope::push_res_with_import()`. // `ItemScope::push_res_with_import()`.
if let Some((def, def_vis, _)) = defs.types { if let Some(def) = defs.types {
if let Some((prev_def, prev_vis, _)) = prev_defs.types { if let Some(prev_def) = prev_defs.types {
if def == prev_def if def.def == prev_def.def
&& self.from_glob_import.contains_type(module_id, name.clone()) && self.from_glob_import.contains_type(module_id, name.clone())
&& def_vis != prev_vis && def.vis != prev_def.vis
&& def_vis.max(prev_vis, &self.def_map) == Some(def_vis) && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis)
{ {
changed = true; changed = true;
// This import is being handled here, don't pass it down to // This import is being handled here, don't pass it down to
@ -1119,41 +1119,41 @@ impl DefCollector<'_> {
defs.types = None; defs.types = None;
self.def_map.modules[module_id] self.def_map.modules[module_id]
.scope .scope
.update_visibility_types(name, def_vis); .update_visibility_types(name, def.vis);
} }
} }
} }
if let Some((def, def_vis, _)) = defs.values { if let Some(def) = defs.values {
if let Some((prev_def, prev_vis, _)) = prev_defs.values { if let Some(prev_def) = prev_defs.values {
if def == prev_def if def.def == prev_def.def
&& self.from_glob_import.contains_value(module_id, name.clone()) && self.from_glob_import.contains_value(module_id, name.clone())
&& def_vis != prev_vis && def.vis != prev_def.vis
&& def_vis.max(prev_vis, &self.def_map) == Some(def_vis) && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis)
{ {
changed = true; changed = true;
// See comment above. // See comment above.
defs.values = None; defs.values = None;
self.def_map.modules[module_id] self.def_map.modules[module_id]
.scope .scope
.update_visibility_values(name, def_vis); .update_visibility_values(name, def.vis);
} }
} }
} }
if let Some((def, def_vis, _)) = defs.macros { if let Some(def) = defs.macros {
if let Some((prev_def, prev_vis, _)) = prev_defs.macros { if let Some(prev_def) = prev_defs.macros {
if def == prev_def if def.def == prev_def.def
&& self.from_glob_import.contains_macro(module_id, name.clone()) && self.from_glob_import.contains_macro(module_id, name.clone())
&& def_vis != prev_vis && def.vis != prev_def.vis
&& def_vis.max(prev_vis, &self.def_map) == Some(def_vis) && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis)
{ {
changed = true; changed = true;
// See comment above. // See comment above.
defs.macros = None; defs.macros = None;
self.def_map.modules[module_id] self.def_map.modules[module_id]
.scope .scope
.update_visibility_macros(name, def_vis); .update_visibility_macros(name, def.vis);
} }
} }
} }

View file

@ -67,8 +67,8 @@ impl PerNs {
db: &dyn DefDatabase, db: &dyn DefDatabase,
expected: Option<MacroSubNs>, expected: Option<MacroSubNs>,
) -> Self { ) -> Self {
self.macros = self.macros.filter(|&(id, _, _)| { self.macros = self.macros.filter(|def| {
let this = MacroSubNs::from_id(db, id); let this = MacroSubNs::from_id(db, def.def);
sub_namespace_match(Some(this), expected) sub_namespace_match(Some(this), expected)
}); });
@ -411,7 +411,7 @@ impl DefMap {
original_module: LocalModuleId, original_module: LocalModuleId,
) -> ResolvePathResult { ) -> ResolvePathResult {
for (i, segment) in segments { for (i, segment) in segments {
let (curr, vis, imp) = match curr_per_ns.take_types_full() { let curr = match curr_per_ns.take_types_full() {
Some(r) => r, Some(r) => r,
None => { None => {
// we still have path segments left, but the path so far // we still have path segments left, but the path so far
@ -424,7 +424,7 @@ impl DefMap {
}; };
// resolve segment in curr // resolve segment in curr
curr_per_ns = match curr { curr_per_ns = match curr.def {
ModuleDefId::ModuleId(module) => { ModuleDefId::ModuleId(module) => {
if module.krate != self.krate { if module.krate != self.krate {
let path = ModPath::from_segments( let path = ModPath::from_segments(
@ -492,7 +492,7 @@ impl DefMap {
Some(res) => res, Some(res) => res,
None => { None => {
return ResolvePathResult::new( return ResolvePathResult::new(
PerNs::types(e.into(), vis, imp), PerNs::types(e.into(), curr.vis, curr.import),
ReachedFixedPoint::Yes, ReachedFixedPoint::Yes,
Some(i), Some(i),
false, false,
@ -510,7 +510,7 @@ impl DefMap {
); );
return ResolvePathResult::new( return ResolvePathResult::new(
PerNs::types(s, vis, imp), PerNs::types(s, curr.vis, curr.import),
ReachedFixedPoint::Yes, ReachedFixedPoint::Yes,
Some(i), Some(i),
false, false,

View file

@ -331,7 +331,7 @@ pub type Ty = ();
} }
for (_, res) in module_data.scope.resolutions() { for (_, res) in module_data.scope.resolutions() {
match res.values.map(|(a, _, _)| a).or(res.types.map(|(a, _, _)| a)).unwrap() { match res.values.map(|it| it.def).or(res.types.map(|it| it.def)).unwrap() {
ModuleDefId::FunctionId(f) => _ = db.function_data(f), ModuleDefId::FunctionId(f) => _ = db.function_data(f),
ModuleDefId::AdtId(adt) => match adt { ModuleDefId::AdtId(adt) => match adt {
AdtId::StructId(it) => _ = db.struct_data(it), AdtId::StructId(it) => _ = db.struct_data(it),

View file

@ -28,11 +28,22 @@ bitflags! {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Item<Def, Import = ImportId> {
pub def: Def,
pub vis: Visibility,
pub import: Option<Import>,
}
pub type TypesItem = Item<ModuleDefId, ImportOrExternCrate>;
pub type ValuesItem = Item<ModuleDefId>;
pub type MacrosItem = Item<MacroId>;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct PerNs { pub struct PerNs {
pub types: Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)>, pub types: Option<TypesItem>,
pub values: Option<(ModuleDefId, Visibility, Option<ImportId>)>, pub values: Option<ValuesItem>,
pub macros: Option<(MacroId, Visibility, Option<ImportId>)>, pub macros: Option<MacrosItem>,
} }
impl PerNs { impl PerNs {
@ -48,29 +59,33 @@ impl PerNs {
PerNs { types: None, values: None, macros: None } PerNs { types: None, values: None, macros: None }
} }
pub fn values(t: ModuleDefId, v: Visibility, i: Option<ImportId>) -> PerNs { pub fn values(def: ModuleDefId, vis: Visibility, import: Option<ImportId>) -> PerNs {
PerNs { types: None, values: Some((t, v, i)), macros: None } PerNs { types: None, values: Some(Item { def, vis, import }), macros: None }
} }
pub fn types(t: ModuleDefId, v: Visibility, i: Option<ImportOrExternCrate>) -> PerNs { pub fn types(def: ModuleDefId, vis: Visibility, import: Option<ImportOrExternCrate>) -> PerNs {
PerNs { types: Some((t, v, i)), values: None, macros: None } PerNs { types: Some(Item { def, vis, import }), values: None, macros: None }
} }
pub fn both( pub fn both(
types: ModuleDefId, types: ModuleDefId,
values: ModuleDefId, values: ModuleDefId,
v: Visibility, vis: Visibility,
i: Option<ImportOrExternCrate>, import: Option<ImportOrExternCrate>,
) -> PerNs { ) -> PerNs {
PerNs { PerNs {
types: Some((types, v, i)), types: Some(Item { def: types, vis, import }),
values: Some((values, v, i.and_then(ImportOrExternCrate::into_import))), values: Some(Item {
def: values,
vis,
import: import.and_then(ImportOrExternCrate::into_import),
}),
macros: None, macros: None,
} }
} }
pub fn macros(macro_: MacroId, v: Visibility, i: Option<ImportId>) -> PerNs { pub fn macros(def: MacroId, vis: Visibility, import: Option<ImportId>) -> PerNs {
PerNs { types: None, values: None, macros: Some((macro_, v, i)) } PerNs { types: None, values: None, macros: Some(Item { def, vis, import }) }
} }
pub fn is_none(&self) -> bool { pub fn is_none(&self) -> bool {
@ -82,43 +97,43 @@ impl PerNs {
} }
pub fn take_types(self) -> Option<ModuleDefId> { pub fn take_types(self) -> Option<ModuleDefId> {
self.types.map(|it| it.0) self.types.map(|it| it.def)
} }
pub fn take_types_full(self) -> Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)> { pub fn take_types_full(self) -> Option<TypesItem> {
self.types self.types
} }
pub fn take_values(self) -> Option<ModuleDefId> { pub fn take_values(self) -> Option<ModuleDefId> {
self.values.map(|it| it.0) self.values.map(|it| it.def)
} }
pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportId>)> { pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportId>)> {
self.values.map(|it| (it.0, it.2)) self.values.map(|it| (it.def, it.import))
} }
pub fn take_macros(self) -> Option<MacroId> { pub fn take_macros(self) -> Option<MacroId> {
self.macros.map(|it| it.0) self.macros.map(|it| it.def)
} }
pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportId>)> { pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportId>)> {
self.macros.map(|it| (it.0, it.2)) self.macros.map(|it| (it.def, it.import))
} }
pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs { pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
let _p = tracing::info_span!("PerNs::filter_visibility").entered(); let _p = tracing::info_span!("PerNs::filter_visibility").entered();
PerNs { PerNs {
types: self.types.filter(|&(_, v, _)| f(v)), types: self.types.filter(|def| f(def.vis)),
values: self.values.filter(|&(_, v, _)| f(v)), values: self.values.filter(|def| f(def.vis)),
macros: self.macros.filter(|&(_, v, _)| f(v)), macros: self.macros.filter(|def| f(def.vis)),
} }
} }
pub fn with_visibility(self, vis: Visibility) -> PerNs { pub fn with_visibility(self, vis: Visibility) -> PerNs {
PerNs { PerNs {
types: self.types.map(|(it, _, c)| (it, vis, c)), types: self.types.map(|def| Item { vis, ..def }),
values: self.values.map(|(it, _, c)| (it, vis, c)), values: self.values.map(|def| Item { vis, ..def }),
macros: self.macros.map(|(it, _, import)| (it, vis, import)), macros: self.macros.map(|def| Item { vis, ..def }),
} }
} }
@ -141,15 +156,17 @@ impl PerNs {
pub fn iter_items(self) -> impl Iterator<Item = (ItemInNs, Option<ImportOrExternCrate>)> { pub fn iter_items(self) -> impl Iterator<Item = (ItemInNs, Option<ImportOrExternCrate>)> {
let _p = tracing::info_span!("PerNs::iter_items").entered(); let _p = tracing::info_span!("PerNs::iter_items").entered();
self.types self.types
.map(|it| (ItemInNs::Types(it.0), it.2)) .map(|it| (ItemInNs::Types(it.def), it.import))
.into_iter() .into_iter()
.chain( .chain(
self.values self.values.map(|it| {
.map(|it| (ItemInNs::Values(it.0), it.2.map(ImportOrExternCrate::Import))), (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::Import))
}),
) )
.chain( .chain(
self.macros self.macros.map(|it| {
.map(|it| (ItemInNs::Macros(it.0), it.2.map(ImportOrExternCrate::Import))), (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::Import))
}),
) )
} }
} }

View file

@ -933,8 +933,8 @@ impl ModuleItemMap {
Some(ResolveValueResult::ValueNs(value, import)) Some(ResolveValueResult::ValueNs(value, import))
} }
Some(idx) => { Some(idx) => {
let (def, _, import) = module_def.take_types_full()?; let def = module_def.take_types_full()?;
let ty = match def { let ty = match def.def {
ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
ModuleDefId::TraitId(it) => TypeNs::TraitId(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it), ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
@ -948,7 +948,7 @@ impl ModuleItemMap {
| ModuleDefId::MacroId(_) | ModuleDefId::MacroId(_)
| ModuleDefId::StaticId(_) => return None, | ModuleDefId::StaticId(_) => return None,
}; };
Some(ResolveValueResult::Partial(ty, idx, import)) Some(ResolveValueResult::Partial(ty, idx, def.import))
} }
} }
} }
@ -986,8 +986,8 @@ fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportId>)> {
} }
fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> { fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
let (def, _, import) = per_ns.take_types_full()?; let def = per_ns.take_types_full()?;
let res = match def { let res = match def.def {
ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it), ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
@ -1003,7 +1003,7 @@ fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
| ModuleDefId::StaticId(_) | ModuleDefId::StaticId(_)
| ModuleDefId::ModuleId(_) => return None, | ModuleDefId::ModuleId(_) => return None,
}; };
Some((res, import)) Some((res, def.import))
} }
#[derive(Default)] #[derive(Default)]
@ -1019,14 +1019,14 @@ impl ScopeNames {
} }
} }
fn add_per_ns(&mut self, name: &Name, def: PerNs) { fn add_per_ns(&mut self, name: &Name, def: PerNs) {
if let &Some((ty, _, _)) = &def.types { if let Some(ty) = &def.types {
self.add(name, ScopeDef::ModuleDef(ty)) self.add(name, ScopeDef::ModuleDef(ty.def))
} }
if let &Some((def, _, _)) = &def.values { if let Some(def) = &def.values {
self.add(name, ScopeDef::ModuleDef(def)) self.add(name, ScopeDef::ModuleDef(def.def))
} }
if let &Some((mac, _, _)) = &def.macros { if let Some(mac) = &def.macros {
self.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac))) self.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac.def)))
} }
if def.is_none() { if def.is_none() {
self.add(name, ScopeDef::Unknown) self.add(name, ScopeDef::Unknown)

View file

@ -153,13 +153,13 @@ fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId) -> SyntaxContextId {
/// This expands the given macro call, but with different arguments. This is /// This expands the given macro call, but with different arguments. This is
/// used for completion, where we want to see what 'would happen' if we insert a /// used for completion, where we want to see what 'would happen' if we insert a
/// token. The `token_to_map` mapped down into the expansion, with the mapped /// token. The `token_to_map` mapped down into the expansion, with the mapped
/// token returned. /// token(s) returned with their priority.
pub fn expand_speculative( pub fn expand_speculative(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
actual_macro_call: MacroCallId, actual_macro_call: MacroCallId,
speculative_args: &SyntaxNode, speculative_args: &SyntaxNode,
token_to_map: SyntaxToken, token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> { ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
let loc = db.lookup_intern_macro_call(actual_macro_call); let loc = db.lookup_intern_macro_call(actual_macro_call);
let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind); let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind);
@ -303,17 +303,19 @@ pub fn expand_speculative(
token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition); token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition);
let syntax_node = node.syntax_node(); let syntax_node = node.syntax_node();
let (token, _) = rev_tmap let token = rev_tmap
.ranges_with_span(span_map.span_for_range(token_to_map.text_range())) .ranges_with_span(span_map.span_for_range(token_to_map.text_range()))
.filter_map(|(range, ctx)| syntax_node.covering_element(range).into_token().zip(Some(ctx))) .filter_map(|(range, ctx)| syntax_node.covering_element(range).into_token().zip(Some(ctx)))
.min_by_key(|(t, ctx)| { .map(|(t, ctx)| {
// prefer tokens of the same kind and text, as well as non opaque marked ones // prefer tokens of the same kind and text, as well as non opaque marked ones
// Note the inversion of the score here, as we want to prefer the first token in case // Note the inversion of the score here, as we want to prefer the first token in case
// of all tokens having the same score // of all tokens having the same score
ctx.is_opaque(db) as u8 let ranking = ctx.is_opaque(db) as u8
+ 2 * (t.kind() != token_to_map.kind()) as u8 + 2 * (t.kind() != token_to_map.kind()) as u8
+ 4 * ((t.text() != token_to_map.text()) as u8) + 4 * ((t.text() != token_to_map.text()) as u8);
})?; (t, ranking)
})
.collect();
Some((node.syntax_node(), token)) Some((node.syntax_node(), token))
} }

View file

@ -22,7 +22,6 @@ use hir_def::{
use crate::{ use crate::{
db::{HirDatabase, InternedCoroutine}, db::{HirDatabase, InternedCoroutine},
display::HirDisplay,
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
generics::generics, generics::generics,
make_binders, make_single_type_binders, make_binders, make_single_type_binders,
@ -823,13 +822,12 @@ pub(crate) fn impl_datum_query(
let _p = tracing::info_span!("impl_datum_query").entered(); let _p = tracing::info_span!("impl_datum_query").entered();
debug!("impl_datum {:?}", impl_id); debug!("impl_datum {:?}", impl_id);
let impl_: hir_def::ImplId = from_chalk(db, impl_id); let impl_: hir_def::ImplId = from_chalk(db, impl_id);
impl_def_datum(db, krate, impl_id, impl_) impl_def_datum(db, krate, impl_)
} }
fn impl_def_datum( fn impl_def_datum(
db: &dyn HirDatabase, db: &dyn HirDatabase,
krate: CrateId, krate: CrateId,
chalk_id: ImplId,
impl_id: hir_def::ImplId, impl_id: hir_def::ImplId,
) -> Arc<ImplDatum> { ) -> Arc<ImplDatum> {
let trait_ref = db let trait_ref = db
@ -850,13 +848,6 @@ fn impl_def_datum(
}; };
let where_clauses = convert_where_clauses(db, impl_id.into(), &bound_vars); let where_clauses = convert_where_clauses(db, impl_id.into(), &bound_vars);
let negative = impl_data.is_negative; let negative = impl_data.is_negative;
debug!(
"impl {:?}: {}{} where {:?}",
chalk_id,
if negative { "!" } else { "" },
trait_ref.display(db, db.crate_graph()[krate].edition),
where_clauses
);
let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive }; let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive };

View file

@ -193,10 +193,19 @@ impl<'a> UnsafeVisitor<'a> {
self.resolver.reset_to_guard(guard); self.resolver.reset_to_guard(guard);
} }
Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => { Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => {
if let Expr::Path(_) = self.body.exprs[*expr] { match self.body.exprs[*expr] {
// Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`, // Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`,
// see https://github.com/rust-lang/rust/pull/125834. // see https://github.com/rust-lang/rust/pull/125834.
return; Expr::Path(_) => return,
// https://github.com/rust-lang/rust/pull/129248
// Taking a raw ref to a deref place expr is always safe.
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
self.body
.walk_child_exprs_without_pats(expr, |child| self.walk_expr(child));
return;
}
_ => (),
} }
} }
Expr::MethodCall { .. } => { Expr::MethodCall { .. } => {

View file

@ -262,7 +262,7 @@ pub struct UnresolvedAssocItem {
#[derive(Debug)] #[derive(Debug)]
pub struct UnresolvedIdent { pub struct UnresolvedIdent {
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>, pub node: InFile<(AstPtr<Either<ast::Expr, ast::Pat>>, Option<TextRange>)>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -550,11 +550,10 @@ impl AnyDiagnostic {
source_map: &hir_def::body::BodySourceMap, source_map: &hir_def::body::BodySourceMap,
) -> Option<AnyDiagnostic> { ) -> Option<AnyDiagnostic> {
let expr_syntax = |expr| { let expr_syntax = |expr| {
source_map.expr_syntax(expr).inspect_err(|_| tracing::error!("synthetic syntax")).ok() source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok()
};
let pat_syntax = |pat| {
source_map.pat_syntax(pat).inspect_err(|_| tracing::error!("synthetic syntax")).ok()
}; };
let pat_syntax =
|pat| source_map.pat_syntax(pat).inspect_err(|_| stdx::never!("synthetic syntax")).ok();
let expr_or_pat_syntax = |id| match id { let expr_or_pat_syntax = |id| match id {
ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(|it| it.map(AstPtr::wrap_left)), ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(|it| it.map(AstPtr::wrap_left)),
ExprOrPatId::PatId(pat) => pat_syntax(pat), ExprOrPatId::PatId(pat) => pat_syntax(pat),
@ -626,8 +625,16 @@ impl AnyDiagnostic {
UnresolvedAssocItem { expr_or_pat }.into() UnresolvedAssocItem { expr_or_pat }.into()
} }
&InferenceDiagnostic::UnresolvedIdent { id } => { &InferenceDiagnostic::UnresolvedIdent { id } => {
let expr_or_pat = expr_or_pat_syntax(id)?; let node = match id {
UnresolvedIdent { expr_or_pat }.into() ExprOrPatId::ExprId(id) => match source_map.expr_syntax(id) {
Ok(syntax) => syntax.map(|it| (it.wrap_left(), None)),
Err(SyntheticSyntax) => source_map
.format_args_implicit_capture(id)?
.map(|(node, range)| (node.wrap_left(), Some(range))),
},
ExprOrPatId::PatId(id) => pat_syntax(id)?.map(|it| (it, None)),
};
UnresolvedIdent { node }.into()
} }
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => { &InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
let expr = expr_syntax(expr)?; let expr = expr_syntax(expr)?;

View file

@ -3105,10 +3105,10 @@ impl From<ModuleDef> for ItemInNs {
} }
impl ItemInNs { impl ItemInNs {
pub fn as_module_def(self) -> Option<ModuleDef> { pub fn into_module_def(self) -> ModuleDef {
match self { match self {
ItemInNs::Types(id) | ItemInNs::Values(id) => Some(id), ItemInNs::Types(id) | ItemInNs::Values(id) => id,
ItemInNs::Macros(_) => None, ItemInNs::Macros(id) => ModuleDef::Macro(id),
} }
} }

View file

@ -38,9 +38,9 @@ use span::{AstIdMap, EditionedFileId, FileId, HirFileIdRepr, SyntaxContextId};
use stdx::TupleExt; use stdx::TupleExt;
use syntax::{ use syntax::{
algo::skip_trivia_token, algo::skip_trivia_token,
ast::{self, HasAttrs as _, HasGenericParams, IsString as _}, ast::{self, HasAttrs as _, HasGenericParams},
AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, AstNode, AstToken, Direction, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
TextSize, TextRange, TextSize,
}; };
use triomphe::Arc; use triomphe::Arc;
@ -571,7 +571,7 @@ impl<'db> SemanticsImpl<'db> {
actual_macro_call: &ast::MacroCall, actual_macro_call: &ast::MacroCall,
speculative_args: &ast::TokenTree, speculative_args: &ast::TokenTree,
token_to_map: SyntaxToken, token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> { ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
let SourceAnalyzer { file_id, resolver, .. } = let SourceAnalyzer { file_id, resolver, .. } =
self.analyze_no_infer(actual_macro_call.syntax())?; self.analyze_no_infer(actual_macro_call.syntax())?;
let macro_call = InFile::new(file_id, actual_macro_call); let macro_call = InFile::new(file_id, actual_macro_call);
@ -592,7 +592,7 @@ impl<'db> SemanticsImpl<'db> {
macro_file: MacroFileId, macro_file: MacroFileId,
speculative_args: &SyntaxNode, speculative_args: &SyntaxNode,
token_to_map: SyntaxToken, token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> { ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
hir_expand::db::expand_speculative( hir_expand::db::expand_speculative(
self.db.upcast(), self.db.upcast(),
macro_file.macro_call_id, macro_file.macro_call_id,
@ -608,7 +608,7 @@ impl<'db> SemanticsImpl<'db> {
actual_macro_call: &ast::Item, actual_macro_call: &ast::Item,
speculative_args: &ast::Item, speculative_args: &ast::Item,
token_to_map: SyntaxToken, token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> { ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
let macro_call = self.wrap_node_infile(actual_macro_call.clone()); let macro_call = self.wrap_node_infile(actual_macro_call.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call.as_ref()))?; let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call.as_ref()))?;
hir_expand::db::expand_speculative( hir_expand::db::expand_speculative(
@ -624,7 +624,7 @@ impl<'db> SemanticsImpl<'db> {
actual_macro_call: &ast::Attr, actual_macro_call: &ast::Attr,
speculative_args: &ast::Attr, speculative_args: &ast::Attr,
token_to_map: SyntaxToken, token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> { ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
let attr = self.wrap_node_infile(actual_macro_call.clone()); let attr = self.wrap_node_infile(actual_macro_call.clone());
let adt = actual_macro_call.syntax().parent().and_then(ast::Adt::cast)?; let adt = actual_macro_call.syntax().parent().and_then(ast::Adt::cast)?;
let macro_call_id = self.with_ctx(|ctx| { let macro_call_id = self.with_ctx(|ctx| {
@ -643,8 +643,7 @@ impl<'db> SemanticsImpl<'db> {
&self, &self,
string: &ast::String, string: &ast::String,
) -> Option<Vec<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)>> { ) -> Option<Vec<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)>> {
let quote = string.open_quote_text_range()?; let string_start = string.syntax().text_range().start();
let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?; let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
self.descend_into_macros_breakable(token, |token, _| { self.descend_into_macros_breakable(token, |token, _| {
(|| { (|| {
@ -658,7 +657,7 @@ impl<'db> SemanticsImpl<'db> {
let format_args = self.wrap_node_infile(format_args); let format_args = self.wrap_node_infile(format_args);
let res = source_analyzer let res = source_analyzer
.as_format_args_parts(self.db, format_args.as_ref())? .as_format_args_parts(self.db, format_args.as_ref())?
.map(|(range, res)| (range + quote.end(), res.map(Either::Left))) .map(|(range, res)| (range + string_start, res.map(Either::Left)))
.collect(); .collect();
Some(res) Some(res)
} else { } else {
@ -672,7 +671,7 @@ impl<'db> SemanticsImpl<'db> {
.iter() .iter()
.map(|&(range, index)| { .map(|&(range, index)| {
( (
range + quote.end(), range + string_start,
Some(Either::Right(InlineAsmOperand { owner, expr, index })), Some(Either::Right(InlineAsmOperand { owner, expr, index })),
) )
}) })
@ -690,17 +689,16 @@ impl<'db> SemanticsImpl<'db> {
original_token: SyntaxToken, original_token: SyntaxToken,
offset: TextSize, offset: TextSize,
) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> { ) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> {
let original_string = ast::String::cast(original_token.clone())?; let string_start = original_token.text_range().start();
let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?; let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
let quote = original_string.open_quote_text_range()?;
self.descend_into_macros_breakable(original_token, |token, _| { self.descend_into_macros_breakable(original_token, |token, _| {
(|| { (|| {
let token = token.value; let token = token.value;
self.resolve_offset_in_format_args( self.resolve_offset_in_format_args(
ast::String::cast(token)?, ast::String::cast(token)?,
offset.checked_sub(quote.end())?, offset.checked_sub(string_start)?,
) )
.map(|(range, res)| (range + quote.end(), res)) .map(|(range, res)| (range + string_start, res))
})() })()
.map_or(ControlFlow::Continue(()), ControlFlow::Break) .map_or(ControlFlow::Continue(()), ControlFlow::Break)
}) })
@ -1542,6 +1540,21 @@ impl<'db> SemanticsImpl<'db> {
Some(items.iter_items().map(|(item, _)| item.into())) Some(items.iter_items().map(|(item, _)| item.into()))
} }
pub fn resolve_mod_path_relative(
&self,
to: Module,
segments: impl IntoIterator<Item = SmolStr>,
) -> Option<impl Iterator<Item = ItemInNs>> {
let items = to.id.resolver(self.db.upcast()).resolve_module_path_in_items(
self.db.upcast(),
&ModPath::from_segments(
hir_def::path::PathKind::Plain,
segments.into_iter().map(|it| Name::new(&it, SyntaxContextId::ROOT)),
),
);
Some(items.iter_items().map(|(item, _)| item.into()))
}
fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> { fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> {
self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit) self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit)
} }

View file

@ -3,6 +3,7 @@
use hir::{FileRange, Semantics}; use hir::{FileRange, Semantics};
use ide_db::EditionedFileId; use ide_db::EditionedFileId;
use ide_db::{label::Label, FileId, RootDatabase}; use ide_db::{label::Label, FileId, RootDatabase};
use syntax::Edition;
use syntax::{ use syntax::{
algo::{self, find_node_at_offset, find_node_at_range}, algo::{self, find_node_at_offset, find_node_at_range},
AstNode, AstToken, Direction, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange, AstNode, AstToken, Direction, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange,
@ -94,6 +95,10 @@ impl<'a> AssistContext<'a> {
self.frange.file_id self.frange.file_id
} }
pub(crate) fn edition(&self) -> Edition {
self.frange.file_id.edition()
}
pub(crate) fn has_empty_selection(&self) -> bool { pub(crate) fn has_empty_selection(&self) -> bool {
self.trimmed_range.is_empty() self.trimmed_range.is_empty()
} }

View file

@ -1,10 +1,12 @@
use ide_db::text_edit::TextRange;
use ide_db::{ use ide_db::{
assists::{AssistId, AssistKind}, assists::{AssistId, AssistKind},
defs::Definition, defs::Definition,
search::{FileReference, SearchScope, UsageSearchResult}, search::{FileReference, SearchScope},
syntax_helpers::suggest_name,
text_edit::TextRange,
}; };
use itertools::Itertools; use itertools::Itertools;
use syntax::SmolStr;
use syntax::{ use syntax::{
ast::{self, make, AstNode, FieldExpr, HasName, IdentPat}, ast::{self, make, AstNode, FieldExpr, HasName, IdentPat},
ted, ted,
@ -122,33 +124,43 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat
return None; return None;
} }
let name = ident_pat.name()?.to_string(); let usages = ctx.sema.to_def(&ident_pat).and_then(|def| {
let usages = ctx.sema.to_def(&ident_pat).map(|def| {
Definition::Local(def) Definition::Local(def)
.usages(&ctx.sema) .usages(&ctx.sema)
.in_scope(&SearchScope::single_file(ctx.file_id())) .in_scope(&SearchScope::single_file(ctx.file_id()))
.all() .all()
.iter()
.next()
.map(|(_, refs)| refs.to_vec())
}); });
let field_names = (0..field_types.len()) let mut name_generator = {
.map(|i| generate_name(ctx, i, &name, &ident_pat, &usages)) let mut names = vec![];
if let Some(scope) = ctx.sema.scope(ident_pat.syntax()) {
scope.process_all_names(&mut |name, scope| {
if let hir::ScopeDef::Local(_) = scope {
names.push(name.as_str().into())
}
})
}
suggest_name::NameGenerator::new_with_names(names.iter().map(|s: &SmolStr| s.as_str()))
};
let field_names = field_types
.into_iter()
.enumerate()
.map(|(id, ty)| {
match name_generator.for_type(&ty, ctx.db(), ctx.edition()) {
Some(name) => name,
None => name_generator.suggest_name(&format!("_{}", id)),
}
.to_string()
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Some(TupleData { ident_pat, ref_type, field_names, usages }) Some(TupleData { ident_pat, ref_type, field_names, usages })
} }
fn generate_name(
_ctx: &AssistContext<'_>,
index: usize,
_tuple_name: &str,
_ident_pat: &IdentPat,
_usages: &Option<UsageSearchResult>,
) -> String {
// FIXME: detect if name already used
format!("_{index}")
}
enum RefType { enum RefType {
ReadOnly, ReadOnly,
Mutable, Mutable,
@ -157,7 +169,7 @@ struct TupleData {
ident_pat: IdentPat, ident_pat: IdentPat,
ref_type: Option<RefType>, ref_type: Option<RefType>,
field_names: Vec<String>, field_names: Vec<String>,
usages: Option<UsageSearchResult>, usages: Option<Vec<FileReference>>,
} }
fn edit_tuple_assignment( fn edit_tuple_assignment(
ctx: &AssistContext<'_>, ctx: &AssistContext<'_>,
@ -213,42 +225,23 @@ fn edit_tuple_usages(
ctx: &AssistContext<'_>, ctx: &AssistContext<'_>,
in_sub_pattern: bool, in_sub_pattern: bool,
) -> Option<Vec<EditTupleUsage>> { ) -> Option<Vec<EditTupleUsage>> {
let mut current_file_usages = None; // We need to collect edits first before actually applying them
// as mapping nodes to their mutable node versions requires an
// unmodified syntax tree.
//
// We also defer editing usages in the current file first since
// tree mutation in the same file breaks when `builder.edit_file`
// is called
if let Some(usages) = data.usages.as_ref() { let edits = data
// We need to collect edits first before actually applying them .usages
// as mapping nodes to their mutable node versions requires an .as_ref()?
// unmodified syntax tree. .as_slice()
// .iter()
// We also defer editing usages in the current file first since .filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern))
// tree mutation in the same file breaks when `builder.edit_file` .collect_vec();
// is called
if let Some((_, refs)) = usages.iter().find(|(file_id, _)| *file_id == ctx.file_id()) { Some(edits)
current_file_usages = Some(
refs.iter()
.filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern))
.collect_vec(),
);
}
for (file_id, refs) in usages.iter() {
if file_id == ctx.file_id() {
continue;
}
edit.edit_file(file_id.file_id());
let tuple_edits = refs
.iter()
.filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern))
.collect_vec();
tuple_edits.into_iter().for_each(|tuple_edit| tuple_edit.apply(edit))
}
}
current_file_usages
} }
fn edit_tuple_usage( fn edit_tuple_usage(
ctx: &AssistContext<'_>, ctx: &AssistContext<'_>,
@ -1769,14 +1762,14 @@ struct S4 {
} }
fn foo() -> Option<()> { fn foo() -> Option<()> {
let ($0_0, _1, _2, _3, _4, _5) = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5); let ($0_0, _1, _2, _3, s4, _5) = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
let v: i32 = *_0; // deref, no parens let v: i32 = *_0; // deref, no parens
let v: &i32 = _0; // no deref, no parens, remove `&` let v: &i32 = _0; // no deref, no parens, remove `&`
f1(*_0); // deref, no parens f1(*_0); // deref, no parens
f2(_0); // `&*` -> cancel out -> no deref, no parens f2(_0); // `&*` -> cancel out -> no deref, no parens
// https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639 // https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639
// let v: i32 = t.1.0; // no deref, no parens // let v: i32 = t.1.0; // no deref, no parens
let v: i32 = _4.value; // no deref, no parens let v: i32 = s4.value; // no deref, no parens
(*_0).do_stuff(); // deref, parens (*_0).do_stuff(); // deref, parens
let v: i32 = (*_2)?; // deref, parens let v: i32 = (*_2)?; // deref, parens
let v: i32 = _3[0]; // no deref, no parens let v: i32 = _3[0]; // no deref, no parens
@ -1815,8 +1808,8 @@ impl S {
} }
fn main() { fn main() {
let ($0_0, _1) = &(S,2); let ($0s, _1) = &(S,2);
let s = _0.f(); let s = s.f();
} }
"#, "#,
) )
@ -1845,8 +1838,8 @@ impl S {
} }
fn main() { fn main() {
let ($0_0, _1) = &(S,2); let ($0s, _1) = &(S,2);
let s = (*_0).f(); let s = (*s).f();
} }
"#, "#,
) )
@ -1882,8 +1875,8 @@ impl T for &S {
} }
fn main() { fn main() {
let ($0_0, _1) = &(S,2); let ($0s, _1) = &(S,2);
let s = (*_0).f(); let s = (*s).f();
} }
"#, "#,
) )
@ -1923,8 +1916,8 @@ impl T for &S {
} }
fn main() { fn main() {
let ($0_0, _1) = &(S,2); let ($0s, _1) = &(S,2);
let s = (*_0).f(); let s = (*s).f();
} }
"#, "#,
) )
@ -1951,8 +1944,8 @@ impl S {
fn do_stuff(&self) -> i32 { 42 } fn do_stuff(&self) -> i32 { 42 }
} }
fn main() { fn main() {
let ($0_0, _1) = &(S,&S); let ($0s, s1) = &(S,&S);
let v = _0.do_stuff(); let v = s.do_stuff();
} }
"#, "#,
) )
@ -1973,7 +1966,7 @@ fn main() {
// `t.0` gets auto-refed -> no deref needed -> no parens // `t.0` gets auto-refed -> no deref needed -> no parens
let v = t.0.do_stuff(); // no deref, no parens let v = t.0.do_stuff(); // no deref, no parens
let v = &t.0.do_stuff(); // `&` is for result -> no deref, no parens let v = &t.0.do_stuff(); // `&` is for result -> no deref, no parens
// deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S` // deref: `s1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
let v = t.1.do_stuff(); // deref, parens let v = t.1.do_stuff(); // deref, parens
} }
"#, "#,
@ -1984,13 +1977,13 @@ impl S {
fn do_stuff(&self) -> i32 { 42 } fn do_stuff(&self) -> i32 { 42 }
} }
fn main() { fn main() {
let ($0_0, _1) = &(S,&S); let ($0s, s1) = &(S,&S);
let v = _0.do_stuff(); // no deref, remove parens let v = s.do_stuff(); // no deref, remove parens
// `t.0` gets auto-refed -> no deref needed -> no parens // `t.0` gets auto-refed -> no deref needed -> no parens
let v = _0.do_stuff(); // no deref, no parens let v = s.do_stuff(); // no deref, no parens
let v = &_0.do_stuff(); // `&` is for result -> no deref, no parens let v = &s.do_stuff(); // `&` is for result -> no deref, no parens
// deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S` // deref: `s1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
let v = (*_1).do_stuff(); // deref, parens let v = (*s1).do_stuff(); // deref, parens
} }
"#, "#,
) )

View file

@ -1,5 +1,8 @@
use hir::{HirDisplay, TypeInfo}; use hir::{HirDisplay, TypeInfo};
use ide_db::{assists::GroupLabel, syntax_helpers::suggest_name}; use ide_db::{
assists::GroupLabel,
syntax_helpers::{suggest_name, LexedStr},
};
use syntax::{ use syntax::{
ast::{ ast::{
self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory, self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
@ -320,24 +323,58 @@ impl ExtractionKind {
ctx: &AssistContext<'_>, ctx: &AssistContext<'_>,
to_extract: &ast::Expr, to_extract: &ast::Expr,
) -> (String, SyntaxNode) { ) -> (String, SyntaxNode) {
let field_shorthand = to_extract // We only do this sort of extraction for fields because they should have lowercase names
.syntax() if let ExtractionKind::Variable = self {
.parent() let field_shorthand = to_extract
.and_then(ast::RecordExprField::cast) .syntax()
.filter(|field| field.name_ref().is_some()); .parent()
let (var_name, expr_replace) = match field_shorthand { .and_then(ast::RecordExprField::cast)
Some(field) => (field.to_string(), field.syntax().clone()), .filter(|field| field.name_ref().is_some());
None => {
(suggest_name::for_variable(to_extract, &ctx.sema), to_extract.syntax().clone()) if let Some(field) = field_shorthand {
return (field.to_string(), field.syntax().clone());
} }
}
let var_name = if let Some(literal_name) = get_literal_name(ctx, to_extract) {
literal_name
} else {
suggest_name::for_variable(to_extract, &ctx.sema)
}; };
let var_name = match self { let var_name = match self {
ExtractionKind::Variable => var_name, ExtractionKind::Variable => var_name.to_lowercase(),
ExtractionKind::Constant | ExtractionKind::Static => var_name.to_uppercase(), ExtractionKind::Constant | ExtractionKind::Static => var_name.to_uppercase(),
}; };
(var_name, expr_replace) (var_name, to_extract.syntax().clone())
}
}
fn get_literal_name(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<String> {
let literal = match expr {
ast::Expr::Literal(literal) => literal,
_ => return None,
};
let inner = match literal.kind() {
ast::LiteralKind::String(string) => string.value().ok()?.into_owned(),
ast::LiteralKind::ByteString(byte_string) => {
String::from_utf8(byte_string.value().ok()?.into_owned()).ok()?
}
ast::LiteralKind::CString(cstring) => {
String::from_utf8(cstring.value().ok()?.into_owned()).ok()?
}
_ => return None,
};
// Entirely arbitrary
if inner.len() > 32 {
return None;
}
match LexedStr::single_token(ctx.file_id().edition(), &inner) {
Some((SyntaxKind::IDENT, None)) => Some(inner),
_ => None,
} }
} }
@ -493,7 +530,7 @@ fn main() {
"#, "#,
r#" r#"
fn main() { fn main() {
let $0var_name = "hello"; let $0hello = "hello";
} }
"#, "#,
"Extract into variable", "Extract into variable",
@ -588,7 +625,7 @@ fn main() {
"#, "#,
r#" r#"
fn main() { fn main() {
const $0VAR_NAME: &str = "hello"; const $0HELLO: &str = "hello";
} }
"#, "#,
"Extract into constant", "Extract into constant",
@ -683,7 +720,7 @@ fn main() {
"#, "#,
r#" r#"
fn main() { fn main() {
static $0VAR_NAME: &str = "hello"; static $0HELLO: &str = "hello";
} }
"#, "#,
"Extract into static", "Extract into static",
@ -2479,4 +2516,120 @@ fn foo() {
"Extract into variable", "Extract into variable",
); );
} }
#[test]
fn extract_string_literal() {
check_assist_by_label(
extract_variable,
r#"
struct Entry(&str);
fn foo() {
let entry = Entry($0"Hello"$0);
}
"#,
r#"
struct Entry(&str);
fn foo() {
let $0hello = "Hello";
let entry = Entry(hello);
}
"#,
"Extract into variable",
);
check_assist_by_label(
extract_variable,
r#"
struct Entry(&str);
fn foo() {
let entry = Entry($0"Hello"$0);
}
"#,
r#"
struct Entry(&str);
fn foo() {
const $0HELLO: &str = "Hello";
let entry = Entry(HELLO);
}
"#,
"Extract into constant",
);
check_assist_by_label(
extract_variable,
r#"
struct Entry(&str);
fn foo() {
let entry = Entry($0"Hello"$0);
}
"#,
r#"
struct Entry(&str);
fn foo() {
static $0HELLO: &str = "Hello";
let entry = Entry(HELLO);
}
"#,
"Extract into static",
);
}
#[test]
fn extract_variable_string_literal_use_field_shorthand() {
// When field shorthand is available, it should
// only be used when extracting into a variable
check_assist_by_label(
extract_variable,
r#"
struct Entry { message: &str }
fn foo() {
let entry = Entry { message: $0"Hello"$0 };
}
"#,
r#"
struct Entry { message: &str }
fn foo() {
let $0message = "Hello";
let entry = Entry { message };
}
"#,
"Extract into variable",
);
check_assist_by_label(
extract_variable,
r#"
struct Entry { message: &str }
fn foo() {
let entry = Entry { message: $0"Hello"$0 };
}
"#,
r#"
struct Entry { message: &str }
fn foo() {
const $0HELLO: &str = "Hello";
let entry = Entry { message: HELLO };
}
"#,
"Extract into constant",
);
check_assist_by_label(
extract_variable,
r#"
struct Entry { message: &str }
fn foo() {
let entry = Entry { message: $0"Hello"$0 };
}
"#,
r#"
struct Entry { message: &str }
fn foo() {
static $0HELLO: &str = "Hello";
let entry = Entry { message: HELLO };
}
"#,
"Extract into static",
);
}
} }

View file

@ -86,7 +86,7 @@ fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option<ItemInNs
} }
fn item_as_assoc(db: &dyn HirDatabase, item: ItemInNs) -> Option<AssocItem> { fn item_as_assoc(db: &dyn HirDatabase, item: ItemInNs) -> Option<AssocItem> {
item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)) item.into_module_def().as_assoc_item(db)
} }
#[cfg(test)] #[cfg(test)]

View file

@ -51,7 +51,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let candidate = import_assets.import_candidate(); let candidate = import_assets.import_candidate();
let qualify_candidate = match syntax_under_caret.clone() { let qualify_candidate = match syntax_under_caret.clone() {
NodeOrToken::Node(syntax_under_caret) => match candidate { NodeOrToken::Node(syntax_under_caret) => match candidate {
ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => { ImportCandidate::Path(candidate) if !candidate.qualifier.is_empty() => {
cov_mark::hit!(qualify_path_qualifier_start); cov_mark::hit!(qualify_path_qualifier_start);
let path = ast::Path::cast(syntax_under_caret)?; let path = ast::Path::cast(syntax_under_caret)?;
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
@ -219,11 +219,9 @@ fn find_trait_method(
} }
fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> { fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
let item_module_def = item.as_module_def()?; match item.into_module_def() {
match item_module_def {
hir::ModuleDef::Trait(trait_) => Some(trait_), hir::ModuleDef::Trait(trait_) => Some(trait_),
_ => item_module_def.as_assoc_item(db)?.container_trait(db), item_module_def => item_module_def.as_assoc_item(db)?.container_trait(db),
} }
} }
@ -247,7 +245,7 @@ fn label(
let import_path = &import.import_path; let import_path = &import.import_path;
match candidate { match candidate {
ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => { ImportCandidate::Path(candidate) if candidate.qualifier.is_empty() => {
format!("Qualify as `{}`", import_path.display(db, edition)) format!("Qualify as `{}`", import_path.display(db, edition))
} }
_ => format!("Qualify with `{}`", import_path.display(db, edition)), _ => format!("Qualify with `{}`", import_path.display(db, edition)),

View file

@ -78,7 +78,7 @@ pub(crate) fn replace_derive_with_manual_impl(
NameToImport::exact_case_sensitive(path.segments().last()?.to_string()), NameToImport::exact_case_sensitive(path.segments().last()?.to_string()),
items_locator::AssocSearchMode::Exclude, items_locator::AssocSearchMode::Exclude,
) )
.filter_map(|item| match item.as_module_def()? { .filter_map(|item| match item.into_module_def() {
ModuleDef::Trait(trait_) => Some(trait_), ModuleDef::Trait(trait_) => Some(trait_),
_ => None, _ => None,
}) })

View file

@ -12,13 +12,15 @@ use syntax::{
use crate::{AssistContext, Assists}; use crate::{AssistContext, Assists};
// FIXME: This ought to be a diagnostic lint.
// Assist: unnecessary_async // Assist: unnecessary_async
// //
// Removes the `async` mark from functions which have no `.await` in their body. // Removes the `async` mark from functions which have no `.await` in their body.
// Looks for calls to the functions and removes the `.await` on the call site. // Looks for calls to the functions and removes the `.await` on the call site.
// //
// ``` // ```
// pub async f$0n foo() {} // pub asy$0nc fn foo() {}
// pub async fn bar() { foo().await } // pub async fn bar() { foo().await }
// ``` // ```
// -> // ->
@ -29,15 +31,11 @@ use crate::{AssistContext, Assists};
pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let function: ast::Fn = ctx.find_node_at_offset()?; let function: ast::Fn = ctx.find_node_at_offset()?;
// Do nothing if the cursor is not on the prototype. This is so that the check does not pollute // Do nothing if the cursor isn't on the async token.
// when the user asks us for assists when in the middle of the function body. let async_token = function.async_token()?;
// We consider the prototype to be anything that is before the body of the function. if !async_token.text_range().contains_inclusive(ctx.offset()) {
let cursor_position = ctx.offset();
if cursor_position >= function.body()?.syntax().text_range().start() {
return None; return None;
} }
// Do nothing if the function isn't async.
function.async_token()?;
// Do nothing if the function has an `await` expression in its body. // Do nothing if the function has an `await` expression in its body.
if function.body()?.syntax().descendants().find_map(ast::AwaitExpr::cast).is_some() { if function.body()?.syntax().descendants().find_map(ast::AwaitExpr::cast).is_some() {
return None; return None;
@ -138,27 +136,22 @@ mod tests {
#[test] #[test]
fn applies_on_empty_function() { fn applies_on_empty_function() {
check_assist(unnecessary_async, "pub async f$0n f() {}", "pub fn f() {}") check_assist(unnecessary_async, "pub asy$0nc fn f() {}", "pub fn f() {}")
} }
#[test] #[test]
fn applies_and_removes_whitespace() { fn applies_and_removes_whitespace() {
check_assist(unnecessary_async, "pub async f$0n f() {}", "pub fn f() {}") check_assist(unnecessary_async, "pub async$0 fn f() {}", "pub fn f() {}")
}
#[test]
fn does_not_apply_on_non_async_function() {
check_assist_not_applicable(unnecessary_async, "pub f$0n f() {}")
} }
#[test] #[test]
fn applies_on_function_with_a_non_await_expr() { fn applies_on_function_with_a_non_await_expr() {
check_assist(unnecessary_async, "pub async f$0n f() { f2() }", "pub fn f() { f2() }") check_assist(unnecessary_async, "pub asy$0nc fn f() { f2() }", "pub fn f() { f2() }")
} }
#[test] #[test]
fn does_not_apply_on_function_with_an_await_expr() { fn does_not_apply_on_function_with_an_await_expr() {
check_assist_not_applicable(unnecessary_async, "pub async f$0n f() { f2().await }") check_assist_not_applicable(unnecessary_async, "pub asy$0nc fn f() { f2().await }")
} }
#[test] #[test]
@ -167,7 +160,7 @@ mod tests {
unnecessary_async, unnecessary_async,
r#" r#"
pub async fn f4() { } pub async fn f4() { }
pub async f$0n f2() { } pub asy$0nc fn f2() { }
pub async fn f() { f2().await } pub async fn f() { f2().await }
pub async fn f3() { f2().await }"#, pub async fn f3() { f2().await }"#,
r#" r#"
@ -184,7 +177,7 @@ pub async fn f3() { f2() }"#,
unnecessary_async, unnecessary_async,
r#" r#"
pub async fn f4() { } pub async fn f4() { }
mod a { pub async f$0n f2() { } } mod a { pub asy$0nc fn f2() { } }
pub async fn f() { a::f2().await } pub async fn f() { a::f2().await }
pub async fn f3() { a::f2().await }"#, pub async fn f3() { a::f2().await }"#,
r#" r#"
@ -202,7 +195,7 @@ pub async fn f3() { a::f2() }"#,
// Ensure that it is the first await on the 3rd line that is removed // Ensure that it is the first await on the 3rd line that is removed
r#" r#"
pub async fn f() { f2().await } pub async fn f() { f2().await }
pub async f$0n f2() -> i32 { 1 } pub asy$0nc fn f2() -> i32 { 1 }
pub async fn f3() { f4(f2().await).await } pub async fn f3() { f4(f2().await).await }
pub async fn f4(i: i32) { }"#, pub async fn f4(i: i32) { }"#,
r#" r#"
@ -220,7 +213,7 @@ pub async fn f4(i: i32) { }"#,
// Ensure that it is the second await on the 3rd line that is removed // Ensure that it is the second await on the 3rd line that is removed
r#" r#"
pub async fn f() { f2().await } pub async fn f() { f2().await }
pub async f$0n f2(i: i32) { } pub async$0 fn f2(i: i32) { }
pub async fn f3() { f2(f4().await).await } pub async fn f3() { f2(f4().await).await }
pub async fn f4() -> i32 { 1 }"#, pub async fn f4() -> i32 { 1 }"#,
r#" r#"
@ -237,7 +230,7 @@ pub async fn f4() -> i32 { 1 }"#,
unnecessary_async, unnecessary_async,
r#" r#"
pub struct S { } pub struct S { }
impl S { pub async f$0n f2(&self) { } } impl S { pub async$0 fn f2(&self) { } }
pub async fn f(s: &S) { s.f2().await }"#, pub async fn f(s: &S) { s.f2().await }"#,
r#" r#"
pub struct S { } pub struct S { }
@ -250,13 +243,13 @@ pub async fn f(s: &S) { s.f2() }"#,
fn does_not_apply_on_function_with_a_nested_await_expr() { fn does_not_apply_on_function_with_a_nested_await_expr() {
check_assist_not_applicable( check_assist_not_applicable(
unnecessary_async, unnecessary_async,
"async f$0n f() { if true { loop { f2().await } } }", "async$0 fn f() { if true { loop { f2().await } } }",
) )
} }
#[test] #[test]
fn does_not_apply_when_not_on_prototype() { fn does_not_apply_when_not_on_async_token() {
check_assist_not_applicable(unnecessary_async, "pub async fn f() { $0f2() }") check_assist_not_applicable(unnecessary_async, "pub async fn$0 f() { f2() }")
} }
#[test] #[test]

View file

@ -198,7 +198,7 @@ fn wrapper_alias(
); );
ctx.sema.resolve_mod_path(ret_type.syntax(), &wrapper_path).and_then(|def| { ctx.sema.resolve_mod_path(ret_type.syntax(), &wrapper_path).and_then(|def| {
def.filter_map(|def| match def.as_module_def()? { def.filter_map(|def| match def.into_module_def() {
hir::ModuleDef::TypeAlias(alias) => { hir::ModuleDef::TypeAlias(alias) => {
let enum_ty = alias.ty(ctx.db()).as_adt()?.as_enum()?; let enum_ty = alias.ty(ctx.db()).as_adt()?.as_enum()?;
(&enum_ty == core_wrapper).then_some(alias) (&enum_ty == core_wrapper).then_some(alias)

View file

@ -3280,7 +3280,7 @@ fn doctest_unnecessary_async() {
check_doc_test( check_doc_test(
"unnecessary_async", "unnecessary_async",
r#####" r#####"
pub async f$0n foo() {} pub asy$0nc fn foo() {}
pub async fn bar() { foo().await } pub async fn bar() { foo().await }
"#####, "#####,
r#####" r#####"

View file

@ -205,7 +205,7 @@ impl S {
fn foo(s: S) { s.$0 } fn foo(s: S) { s.$0 }
"#, "#,
expect![[r#" expect![[r#"
fd foo u32 fd foo u32
me bar() fn(&self) me bar() fn(&self)
"#]], "#]],
); );
@ -259,7 +259,7 @@ impl S {
"#, "#,
expect![[r#" expect![[r#"
fd the_field (u32,) fd the_field (u32,)
me foo() fn(self) me foo() fn(self)
"#]], "#]],
) )
} }
@ -275,7 +275,7 @@ impl A {
"#, "#,
expect![[r#" expect![[r#"
fd the_field (u32, i32) fd the_field (u32, i32)
me foo() fn(&self) me foo() fn(&self)
"#]], "#]],
) )
} }
@ -536,7 +536,7 @@ impl A {
} }
"#, "#,
expect![[r#" expect![[r#"
fd pub_field u32 fd pub_field u32
me pub_method() fn(&self) me pub_method() fn(&self)
"#]], "#]],
) )
@ -550,7 +550,7 @@ union U { field: u8, other: u16 }
fn foo(u: U) { u.$0 } fn foo(u: U) { u.$0 }
"#, "#,
expect![[r#" expect![[r#"
fd field u8 fd field u8
fd other u16 fd other u16
"#]], "#]],
); );
@ -725,8 +725,8 @@ fn test(a: A) {
} }
"#, "#,
expect![[r#" expect![[r#"
fd another u32 fd another u32
fd field u8 fd field u8
me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
"#]], "#]],
); );
@ -748,8 +748,8 @@ fn test(a: A) {
} }
"#, "#,
expect![[r#" expect![[r#"
fd 0 u8 fd 0 u8
fd 1 u32 fd 1 u32
me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
"#]], "#]],
); );
@ -770,8 +770,8 @@ fn test(a: A) {
} }
"#, "#,
expect![[r#" expect![[r#"
fd 0 u8 fd 0 u8
fd 1 u32 fd 1 u32
me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
"#]], "#]],
); );
@ -964,12 +964,12 @@ struct Foo { field: i32 }
impl Foo { fn foo(&self) { $0 } }"#, impl Foo { fn foo(&self) { $0 } }"#,
expect![[r#" expect![[r#"
fd self.field i32 fd self.field i32
me self.foo() fn(&self) me self.foo() fn(&self)
lc self &Foo lc self &Foo
sp Self Foo sp Self Foo
st Foo Foo st Foo Foo
bt u32 u32 bt u32 u32
"#]], "#]],
); );
check( check(
@ -978,12 +978,12 @@ struct Foo(i32);
impl Foo { fn foo(&mut self) { $0 } }"#, impl Foo { fn foo(&mut self) { $0 } }"#,
expect![[r#" expect![[r#"
fd self.0 i32 fd self.0 i32
me self.foo() fn(&mut self) me self.foo() fn(&mut self)
lc self &mut Foo lc self &mut Foo
sp Self Foo sp Self Foo
st Foo Foo st Foo Foo
bt u32 u32 bt u32 u32
"#]], "#]],
); );
} }
@ -1106,7 +1106,7 @@ fn test(a: A) {
} }
"#, "#,
expect![[r#" expect![[r#"
fd 0 u8 fd 0 u8
me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
"#]], "#]],
); );
@ -1162,7 +1162,7 @@ impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> {
} }
"#, "#,
expect![[r#" expect![[r#"
fd foo &u8 fd foo &u8
me foobar() fn(&self) me foobar() fn(&self)
"#]], "#]],
); );
@ -1199,8 +1199,8 @@ impl<B: Bar, F: core::ops::Deref<Target = B>> Foo<F> {
} }
"#, "#,
expect![[r#" expect![[r#"
fd foo &u8 fd foo &u8
"#]], "#]],
); );
} }

View file

@ -537,10 +537,10 @@ impl Test for T {
} }
", ",
expect![[r#" expect![[r#"
sp Self T sp Self T
st T T st T T
tt Test tt Test
bt u32 u32 bt u32 u32
"#]], "#]],
); );
@ -646,10 +646,10 @@ impl Test for T {
} }
", ",
expect![[r#" expect![[r#"
sp Self T sp Self T
st T T st T T
tt Test tt Test
bt u32 u32 bt u32 u32
"#]], "#]],
); );
@ -663,10 +663,10 @@ impl Test for T {
} }
", ",
expect![[r#" expect![[r#"
sp Self T sp Self T
st T T st T T
tt Test tt Test
bt u32 u32 bt u32 u32
"#]], "#]],
); );
@ -682,10 +682,10 @@ impl Test for T {
} }
", ",
expect![[r#" expect![[r#"
sp Self T sp Self T
st T T st T T
tt Test tt Test
bt u32 u32 bt u32 u32
"#]], "#]],
); );
@ -730,10 +730,10 @@ impl Test for T {
} }
", ",
expect![[r#" expect![[r#"
sp Self T sp Self T
st T T st T T
tt Test tt Test
bt u32 u32 bt u32 u32
"#]], "#]],
); );

View file

@ -78,19 +78,19 @@ fn foo(a: A) { a.$0 }
"#, "#,
expect![[r#" expect![[r#"
me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
kw await expr.await kw await expr.await
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
); );
@ -105,19 +105,19 @@ fn foo() {
"#, "#,
expect![[r#" expect![[r#"
me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
kw await expr.await kw await expr.await
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
); );
} }
@ -134,19 +134,19 @@ fn foo(a: A) { a.$0 }
"#, "#,
expect![[r#" expect![[r#"
me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
kw await expr.await kw await expr.await
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
); );
} }

View file

@ -423,21 +423,21 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn if if expr {} sn if if expr {}
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn not !expr sn not !expr
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
sn while while expr {} sn while while expr {}
"#]], "#]],
); );
} }
@ -456,19 +456,19 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn if if expr {} sn if if expr {}
sn match match expr {} sn match match expr {}
sn not !expr sn not !expr
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
sn while while expr {} sn while while expr {}
"#]], "#]],
); );
} }
@ -483,18 +483,18 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
) )
} }
@ -509,21 +509,21 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn if if expr {} sn if if expr {}
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn not !expr sn not !expr
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
sn while while expr {} sn while while expr {}
"#]], "#]],
); );
} }

View file

@ -718,7 +718,7 @@ impl<'a> CompletionContext<'a> {
expected: (expected_type, expected_name), expected: (expected_type, expected_name),
qualifier_ctx, qualifier_ctx,
token, token,
offset, original_offset,
} = expand_and_analyze( } = expand_and_analyze(
&sema, &sema,
original_file.syntax().clone(), original_file.syntax().clone(),
@ -728,7 +728,7 @@ impl<'a> CompletionContext<'a> {
)?; )?;
// adjust for macro input, this still fails if there is no token written yet // adjust for macro input, this still fails if there is no token written yet
let scope = sema.scope_at_offset(&token.parent()?, offset)?; let scope = sema.scope_at_offset(&token.parent()?, original_offset)?;
let krate = scope.krate(); let krate = scope.krate();
let module = scope.module(); let module = scope.module();

View file

@ -22,10 +22,14 @@ use crate::context::{
COMPLETION_MARKER, COMPLETION_MARKER,
}; };
#[derive(Debug)]
struct ExpansionResult { struct ExpansionResult {
original_file: SyntaxNode, original_file: SyntaxNode,
speculative_file: SyntaxNode, speculative_file: SyntaxNode,
offset: TextSize, /// The offset in the original file.
original_offset: TextSize,
/// The offset in the speculatively expanded file.
speculative_offset: TextSize,
fake_ident_token: SyntaxToken, fake_ident_token: SyntaxToken,
derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>, derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
} }
@ -36,7 +40,8 @@ pub(super) struct AnalysisResult {
pub(super) qualifier_ctx: QualifierCtx, pub(super) qualifier_ctx: QualifierCtx,
/// the original token of the expanded file /// the original token of the expanded file
pub(super) token: SyntaxToken, pub(super) token: SyntaxToken,
pub(super) offset: TextSize, /// The offset in the original file.
pub(super) original_offset: TextSize,
} }
pub(super) fn expand_and_analyze( pub(super) fn expand_and_analyze(
@ -54,226 +59,344 @@ pub(super) fn expand_and_analyze(
// make the offset point to the start of the original token, as that is what the // make the offset point to the start of the original token, as that is what the
// intermediate offsets calculated in expansion always points to // intermediate offsets calculated in expansion always points to
let offset = offset - relative_offset; let offset = offset - relative_offset;
let expansion = let expansion = expand(
expand(sema, original_file, speculative_file, offset, fake_ident_token, relative_offset); sema,
original_file.clone(),
speculative_file.clone(),
offset,
fake_ident_token.clone(),
relative_offset,
)
.unwrap_or(ExpansionResult {
original_file,
speculative_file,
original_offset: offset,
speculative_offset: fake_ident_token.text_range().start(),
fake_ident_token,
derive_ctx: None,
});
// add the relative offset back, so that left_biased finds the proper token // add the relative offset back, so that left_biased finds the proper token
let offset = expansion.offset + relative_offset; let original_offset = expansion.original_offset + relative_offset;
let token = expansion.original_file.token_at_offset(offset).left_biased()?; let token = expansion.original_file.token_at_offset(original_offset).left_biased()?;
analyze(sema, expansion, original_token, &token).map(|(analysis, expected, qualifier_ctx)| { analyze(sema, expansion, original_token, &token).map(|(analysis, expected, qualifier_ctx)| {
AnalysisResult { analysis, expected, qualifier_ctx, token, offset } AnalysisResult { analysis, expected, qualifier_ctx, token, original_offset }
}) })
} }
/// Expand attributes and macro calls at the current cursor position for both the original file /// Expand attributes and macro calls at the current cursor position for both the original file
/// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
/// and speculative states stay in sync. /// and speculative states stay in sync.
///
/// We do this by recursively expanding all macros and picking the best possible match. We cannot just
/// choose the first expansion each time because macros can expand to something that does not include
/// our completion marker, e.g.:
/// ```
/// macro_rules! helper { ($v:ident) => {} }
/// macro_rules! my_macro {
/// ($v:ident) => {
/// helper!($v);
/// $v
/// };
/// }
///
/// my_macro!(complete_me_here)
/// ```
/// If we would expand the first thing we encounter only (which in fact this method used to do), we would
/// be unable to complete here, because we would be walking directly into the void. So we instead try
/// *every* possible path.
///
/// This can also creates discrepancies between the speculative and real expansions: because we insert
/// tokens, we insert characters, which means if we try the second occurrence it may not be at the same
/// position in the original and speculative file. We take an educated guess here, and for each token
/// that we check, we subtract `COMPLETION_MARKER.len()`. This may not be accurate because proc macros
/// can insert the text of the completion marker in other places while removing the span, but this is
/// the best we can do.
fn expand( fn expand(
sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
mut original_file: SyntaxNode, original_file: SyntaxNode,
mut speculative_file: SyntaxNode, speculative_file: SyntaxNode,
mut offset: TextSize, original_offset: TextSize,
mut fake_ident_token: SyntaxToken, fake_ident_token: SyntaxToken,
relative_offset: TextSize, relative_offset: TextSize,
) -> ExpansionResult { ) -> Option<ExpansionResult> {
let _p = tracing::info_span!("CompletionContext::expand").entered(); let _p = tracing::info_span!("CompletionContext::expand").entered();
let mut derive_ctx = None;
'expansion: loop { if !sema.might_be_inside_macro_call(&fake_ident_token)
let parent_item = && original_file
|item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); .token_at_offset(original_offset + relative_offset)
let ancestor_items = iter::successors( .right_biased()
Option::zip( .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token))
find_node_at_offset::<ast::Item>(&original_file, offset), {
find_node_at_offset::<ast::Item>(&speculative_file, offset), // Recursion base case.
return Some(ExpansionResult {
original_file,
speculative_file,
original_offset,
speculative_offset: fake_ident_token.text_range().start(),
fake_ident_token,
derive_ctx: None,
});
}
let parent_item =
|item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
let ancestor_items = iter::successors(
Option::zip(
find_node_at_offset::<ast::Item>(&original_file, original_offset),
find_node_at_offset::<ast::Item>(
&speculative_file,
fake_ident_token.text_range().start(),
), ),
|(a, b)| parent_item(a).zip(parent_item(b)), ),
); |(a, b)| parent_item(a).zip(parent_item(b)),
);
// first try to expand attributes as these are always the outermost macro calls // first try to expand attributes as these are always the outermost macro calls
'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items { 'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items {
match ( match (
sema.expand_attr_macro(&actual_item), sema.expand_attr_macro(&actual_item),
sema.speculative_expand_attr_macro( sema.speculative_expand_attr_macro(
&actual_item, &actual_item,
&item_with_fake_ident, &item_with_fake_ident,
fake_ident_token.clone(), fake_ident_token.clone(),
), ),
) { ) {
// maybe parent items have attributes, so continue walking the ancestors // maybe parent items have attributes, so continue walking the ancestors
(None, None) => continue 'ancestors, (None, None) => continue 'ancestors,
// successful expansions // successful expansions
( (
Some(ExpandResult { value: actual_expansion, err: _ }), Some(ExpandResult { value: actual_expansion, err: _ }),
Some((fake_expansion, fake_mapped_token)), Some((fake_expansion, fake_mapped_tokens)),
) => { ) => {
let new_offset = fake_mapped_token.text_range().start(); let mut accumulated_offset_from_fake_tokens = 0;
if new_offset + relative_offset > actual_expansion.text_range().end() { let actual_range = actual_expansion.text_range().end();
// offset outside of bounds from the original expansion, let result = fake_mapped_tokens
// stop here to prevent problems from happening .into_iter()
break 'expansion; .filter_map(|(fake_mapped_token, rank)| {
} let accumulated_offset = accumulated_offset_from_fake_tokens;
original_file = actual_expansion; if !fake_mapped_token.text().contains(COMPLETION_MARKER) {
speculative_file = fake_expansion; // Proc macros can make the same span with different text, we don't
fake_ident_token = fake_mapped_token; // want them to participate in completion because the macro author probably
offset = new_offset; // didn't intend them to.
continue 'expansion; return None;
}
accumulated_offset_from_fake_tokens += COMPLETION_MARKER.len();
let new_offset = fake_mapped_token.text_range().start()
- TextSize::new(accumulated_offset as u32);
if new_offset + relative_offset > actual_range {
// offset outside of bounds from the original expansion,
// stop here to prevent problems from happening
return None;
}
let result = expand(
sema,
actual_expansion.clone(),
fake_expansion.clone(),
new_offset,
fake_mapped_token,
relative_offset,
)?;
Some((result, rank))
})
.min_by_key(|(_, rank)| *rank)
.map(|(result, _)| result);
if result.is_some() {
return result;
} }
// exactly one expansion failed, inconsistent state so stop expanding completely }
_ => break 'expansion, // exactly one expansion failed, inconsistent state so stop expanding completely
_ => break 'ancestors,
}
}
// No attributes have been expanded, so look for macro_call! token trees or derive token trees
let orig_tt = ancestors_at_offset(&original_file, original_offset)
.map_while(Either::<ast::TokenTree, ast::Meta>::cast)
.last()?;
let spec_tt = ancestors_at_offset(&speculative_file, fake_ident_token.text_range().start())
.map_while(Either::<ast::TokenTree, ast::Meta>::cast)
.last()?;
let (tts, attrs) = match (orig_tt, spec_tt) {
(Either::Left(orig_tt), Either::Left(spec_tt)) => {
let attrs = orig_tt
.syntax()
.parent()
.and_then(ast::Meta::cast)
.and_then(|it| it.parent_attr())
.zip(
spec_tt
.syntax()
.parent()
.and_then(ast::Meta::cast)
.and_then(|it| it.parent_attr()),
);
(Some((orig_tt, spec_tt)), attrs)
}
(Either::Right(orig_path), Either::Right(spec_path)) => {
(None, orig_path.parent_attr().zip(spec_path.parent_attr()))
}
_ => return None,
};
// Expand pseudo-derive expansion aka `derive(Debug$0)`
if let Some((orig_attr, spec_attr)) = attrs {
if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_tokens))) = (
sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
sema.speculative_expand_derive_as_pseudo_attr_macro(
&orig_attr,
&spec_attr,
fake_ident_token.clone(),
),
) {
if let Some((fake_mapped_token, _)) =
fake_mapped_tokens.into_iter().min_by_key(|(_, rank)| *rank)
{
return Some(ExpansionResult {
original_file,
speculative_file,
original_offset,
speculative_offset: fake_ident_token.text_range().start(),
fake_ident_token,
derive_ctx: Some((
actual_expansion,
fake_expansion,
fake_mapped_token.text_range().start(),
orig_attr,
)),
});
} }
} }
// No attributes have been expanded, so look for macro_call! token trees or derive token trees if let Some(spec_adt) =
let orig_tt = match ancestors_at_offset(&original_file, offset) spec_attr.syntax().ancestors().find_map(ast::Item::cast).and_then(|it| match it {
.map_while(Either::<ast::TokenTree, ast::Meta>::cast) ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
.last() ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
ast::Item::Union(it) => Some(ast::Adt::Union(it)),
_ => None,
})
{ {
Some(it) => it, // might be the path of derive helper or a token tree inside of one
None => break 'expansion, if let Some(helpers) = sema.derive_helper(&orig_attr) {
}; for (_mac, file) in helpers {
let spec_tt = match ancestors_at_offset(&speculative_file, offset) if let Some((fake_expansion, fake_mapped_tokens)) = sema.speculative_expand_raw(
.map_while(Either::<ast::TokenTree, ast::Meta>::cast) file,
.last() spec_adt.syntax(),
{ fake_ident_token.clone(),
Some(it) => it, ) {
None => break 'expansion, // we are inside a derive helper token tree, treat this as being inside
}; // the derive expansion
let actual_expansion = sema.parse_or_expand(file.into());
let mut accumulated_offset_from_fake_tokens = 0;
let actual_range = actual_expansion.text_range().end();
let result = fake_mapped_tokens
.into_iter()
.filter_map(|(fake_mapped_token, rank)| {
let accumulated_offset = accumulated_offset_from_fake_tokens;
if !fake_mapped_token.text().contains(COMPLETION_MARKER) {
// Proc macros can make the same span with different text, we don't
// want them to participate in completion because the macro author probably
// didn't intend them to.
return None;
}
accumulated_offset_from_fake_tokens += COMPLETION_MARKER.len();
let (tts, attrs) = match (orig_tt, spec_tt) { let new_offset = fake_mapped_token.text_range().start()
(Either::Left(orig_tt), Either::Left(spec_tt)) => { - TextSize::new(accumulated_offset as u32);
let attrs = orig_tt if new_offset + relative_offset > actual_range {
.syntax() // offset outside of bounds from the original expansion,
.parent() // stop here to prevent problems from happening
.and_then(ast::Meta::cast) return None;
.and_then(|it| it.parent_attr()) }
.zip( let result = expand(
spec_tt sema,
.syntax() actual_expansion.clone(),
.parent() fake_expansion.clone(),
.and_then(ast::Meta::cast) new_offset,
.and_then(|it| it.parent_attr()), fake_mapped_token,
); relative_offset,
(Some((orig_tt, spec_tt)), attrs) )?;
} Some((result, rank))
(Either::Right(orig_path), Either::Right(spec_path)) => { })
(None, orig_path.parent_attr().zip(spec_path.parent_attr())) .min_by_key(|(_, rank)| *rank)
} .map(|(result, _)| result);
_ => break 'expansion, if result.is_some() {
}; return result;
// Expand pseudo-derive expansion aka `derive(Debug$0)`
if let Some((orig_attr, spec_attr)) = attrs {
if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = (
sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
sema.speculative_expand_derive_as_pseudo_attr_macro(
&orig_attr,
&spec_attr,
fake_ident_token.clone(),
),
) {
derive_ctx = Some((
actual_expansion,
fake_expansion,
fake_mapped_token.text_range().start(),
orig_attr,
));
break 'expansion;
}
if let Some(spec_adt) =
spec_attr.syntax().ancestors().find_map(ast::Item::cast).and_then(|it| match it {
ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
ast::Item::Union(it) => Some(ast::Adt::Union(it)),
_ => None,
})
{
// might be the path of derive helper or a token tree inside of one
if let Some(helpers) = sema.derive_helper(&orig_attr) {
for (_mac, file) in helpers {
if let Some((fake_expansion, fake_mapped_token)) = sema
.speculative_expand_raw(
file,
spec_adt.syntax(),
fake_ident_token.clone(),
)
{
// we are inside a derive helper token tree, treat this as being inside
// the derive expansion
let actual_expansion = sema.parse_or_expand(file.into());
let new_offset = fake_mapped_token.text_range().start();
if new_offset + relative_offset > actual_expansion.text_range().end() {
// offset outside of bounds from the original expansion,
// stop here to prevent problems from happening
break 'expansion;
}
original_file = actual_expansion;
speculative_file = fake_expansion;
fake_ident_token = fake_mapped_token;
offset = new_offset;
continue 'expansion;
} }
} }
} }
} }
// at this point we won't have any more successful expansions, so stop
break 'expansion;
} }
// at this point we won't have any more successful expansions, so stop
// Expand fn-like macro calls return None;
let Some((orig_tt, spec_tt)) = tts else { break 'expansion };
if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
orig_tt.syntax().parent().and_then(ast::MacroCall::cast),
spec_tt.syntax().parent().and_then(ast::MacroCall::cast),
) {
let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
let mac_call_path1 =
macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text());
// inconsistent state, stop expanding
if mac_call_path0 != mac_call_path1 {
break 'expansion;
}
let speculative_args = match macro_call_with_fake_ident.token_tree() {
Some(tt) => tt,
None => break 'expansion,
};
match (
sema.expand_macro_call(&actual_macro_call),
sema.speculative_expand_macro_call(
&actual_macro_call,
&speculative_args,
fake_ident_token.clone(),
),
) {
// successful expansions
(Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
let new_offset = fake_mapped_token.text_range().start();
if new_offset + relative_offset > actual_expansion.text_range().end() {
// offset outside of bounds from the original expansion,
// stop here to prevent problems from happening
break 'expansion;
}
original_file = actual_expansion;
speculative_file = fake_expansion;
fake_ident_token = fake_mapped_token;
offset = new_offset;
continue 'expansion;
}
// at least on expansion failed, we won't have anything to expand from this point
// onwards so break out
_ => break 'expansion,
}
}
// none of our states have changed so stop the loop
break 'expansion;
} }
ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx } // Expand fn-like macro calls
let (orig_tt, spec_tt) = tts?;
let (actual_macro_call, macro_call_with_fake_ident) = (
orig_tt.syntax().parent().and_then(ast::MacroCall::cast)?,
spec_tt.syntax().parent().and_then(ast::MacroCall::cast)?,
);
let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
let mac_call_path1 = macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text());
// inconsistent state, stop expanding
if mac_call_path0 != mac_call_path1 {
return None;
}
let speculative_args = macro_call_with_fake_ident.token_tree()?;
match (
sema.expand_macro_call(&actual_macro_call),
sema.speculative_expand_macro_call(
&actual_macro_call,
&speculative_args,
fake_ident_token.clone(),
),
) {
// successful expansions
(Some(actual_expansion), Some((fake_expansion, fake_mapped_tokens))) => {
let mut accumulated_offset_from_fake_tokens = 0;
let actual_range = actual_expansion.text_range().end();
fake_mapped_tokens
.into_iter()
.filter_map(|(fake_mapped_token, rank)| {
let accumulated_offset = accumulated_offset_from_fake_tokens;
if !fake_mapped_token.text().contains(COMPLETION_MARKER) {
// Proc macros can make the same span with different text, we don't
// want them to participate in completion because the macro author probably
// didn't intend them to.
return None;
}
accumulated_offset_from_fake_tokens += COMPLETION_MARKER.len();
let new_offset = fake_mapped_token.text_range().start()
- TextSize::new(accumulated_offset as u32);
if new_offset + relative_offset > actual_range {
// offset outside of bounds from the original expansion,
// stop here to prevent problems from happening
return None;
}
let result = expand(
sema,
actual_expansion.clone(),
fake_expansion.clone(),
new_offset,
fake_mapped_token,
relative_offset,
)?;
Some((result, rank))
})
.min_by_key(|(_, rank)| *rank)
.map(|(result, _)| result)
}
// at least one expansion failed, we won't have anything to expand from this point
// onwards so break out
_ => None,
}
} }
/// Fill the completion context, this is what does semantic reasoning about the surrounding context /// Fill the completion context, this is what does semantic reasoning about the surrounding context
@ -285,8 +408,14 @@ fn analyze(
self_token: &SyntaxToken, self_token: &SyntaxToken,
) -> Option<(CompletionAnalysis, (Option<Type>, Option<ast::NameOrNameRef>), QualifierCtx)> { ) -> Option<(CompletionAnalysis, (Option<Type>, Option<ast::NameOrNameRef>), QualifierCtx)> {
let _p = tracing::info_span!("CompletionContext::analyze").entered(); let _p = tracing::info_span!("CompletionContext::analyze").entered();
let ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx } = let ExpansionResult {
expansion_result; original_file,
speculative_file,
original_offset: _,
speculative_offset,
fake_ident_token,
derive_ctx,
} = expansion_result;
// Overwrite the path kind for derives // Overwrite the path kind for derives
if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx { if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
@ -294,7 +423,8 @@ fn analyze(
find_node_at_offset(&file_with_fake_ident, offset) find_node_at_offset(&file_with_fake_ident, offset)
{ {
let parent = name_ref.syntax().parent()?; let parent = name_ref.syntax().parent()?;
let (mut nameref_ctx, _) = classify_name_ref(sema, &original_file, name_ref, parent)?; let (mut nameref_ctx, _) =
classify_name_ref(sema, &original_file, name_ref, offset, parent)?;
if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind { if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind {
path_ctx.kind = PathKind::Derive { path_ctx.kind = PathKind::Derive {
existing_derives: sema existing_derives: sema
@ -314,7 +444,7 @@ fn analyze(
return None; return None;
} }
let Some(name_like) = find_node_at_offset(&speculative_file, offset) else { let Some(name_like) = find_node_at_offset(&speculative_file, speculative_offset) else {
let analysis = if let Some(original) = ast::String::cast(original_token.clone()) { let analysis = if let Some(original) = ast::String::cast(original_token.clone()) {
CompletionAnalysis::String { original, expanded: ast::String::cast(self_token.clone()) } CompletionAnalysis::String { original, expanded: ast::String::cast(self_token.clone()) }
} else { } else {
@ -350,8 +480,13 @@ fn analyze(
} }
ast::NameLike::NameRef(name_ref) => { ast::NameLike::NameRef(name_ref) => {
let parent = name_ref.syntax().parent()?; let parent = name_ref.syntax().parent()?;
let (nameref_ctx, qualifier_ctx) = let (nameref_ctx, qualifier_ctx) = classify_name_ref(
classify_name_ref(sema, &original_file, name_ref, parent)?; sema,
&original_file,
name_ref,
expansion_result.original_offset,
parent,
)?;
if let NameRefContext { if let NameRefContext {
kind: kind:
@ -636,9 +771,10 @@ fn classify_name_ref(
sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
original_file: &SyntaxNode, original_file: &SyntaxNode,
name_ref: ast::NameRef, name_ref: ast::NameRef,
original_offset: TextSize,
parent: SyntaxNode, parent: SyntaxNode,
) -> Option<(NameRefContext, QualifierCtx)> { ) -> Option<(NameRefContext, QualifierCtx)> {
let nameref = find_node_at_offset(original_file, name_ref.syntax().text_range().start()); let nameref = find_node_at_offset(original_file, original_offset);
let make_res = |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default()); let make_res = |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
@ -760,7 +896,7 @@ fn classify_name_ref(
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body. // We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
// ex. trait Foo $0 {} // ex. trait Foo $0 {}
// in these cases parser recovery usually kicks in for our inserted identifier, causing it // in these cases parser recovery usually kicks in for our inserted identifier, causing it
// to either be parsed as an ExprStmt or a MacroCall, depending on whether it is in a block // to either be parsed as an ExprStmt or a ItemRecovery, depending on whether it is in a block
// expression or an item list. // expression or an item list.
// The following code checks if the body is missing, if it is we either cut off the body // The following code checks if the body is missing, if it is we either cut off the body
// from the item or it was missing in the first place // from the item or it was missing in the first place
@ -1088,15 +1224,10 @@ fn classify_name_ref(
PathKind::Type { location: location.unwrap_or(TypeLocation::Other) } PathKind::Type { location: location.unwrap_or(TypeLocation::Other) }
}; };
let mut kind_macro_call = |it: ast::MacroCall| { let kind_item = |it: &SyntaxNode| {
path_ctx.has_macro_bang = it.excl_token().is_some(); let parent = it.parent()?;
let parent = it.syntax().parent()?;
// Any path in an item list will be treated as a macro call by the parser
let kind = match_ast! { let kind = match_ast! {
match parent { match parent {
ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
ast::MacroType(ty) => make_path_kind_type(ty.into()),
ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module }, ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() { ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
Some(it) => match_ast! { Some(it) => match_ast! {
@ -1126,6 +1257,23 @@ fn classify_name_ref(
}; };
Some(kind) Some(kind)
}; };
let mut kind_macro_call = |it: ast::MacroCall| {
path_ctx.has_macro_bang = it.excl_token().is_some();
let parent = it.syntax().parent()?;
if let Some(kind) = kind_item(it.syntax()) {
return Some(kind);
}
let kind = match_ast! {
match parent {
ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
ast::MacroType(ty) => make_path_kind_type(ty.into()),
_ => return None,
}
};
Some(kind)
};
let make_path_kind_attr = |meta: ast::Meta| { let make_path_kind_attr = |meta: ast::Meta| {
let attr = meta.parent_attr()?; let attr = meta.parent_attr()?;
let kind = attr.kind(); let kind = attr.kind();
@ -1153,94 +1301,98 @@ fn classify_name_ref(
// Infer the path kind // Infer the path kind
let parent = path.syntax().parent()?; let parent = path.syntax().parent()?;
let kind = match_ast! { let kind = 'find_kind: {
match parent { if parent.kind() == SyntaxKind::ERROR {
ast::PathType(it) => make_path_kind_type(it.into()), if let Some(kind) = inbetween_body_and_decl_check(parent.clone()) {
ast::PathExpr(it) => { return Some(make_res(NameRefKind::Keyword(kind)));
if let Some(p) = it.syntax().parent() { }
let p_kind = p.kind();
// The syntax node of interest, for which we want to check whether break 'find_kind kind_item(&parent)?;
// it is sandwiched between an item decl signature and its body. }
let probe = if ast::ExprStmt::can_cast(p_kind) { match_ast! {
Some(p) match parent {
} else if ast::StmtList::can_cast(p_kind) { ast::PathType(it) => make_path_kind_type(it.into()),
Some(it.syntax().clone()) ast::PathExpr(it) => {
} else { if let Some(p) = it.syntax().parent() {
None let p_kind = p.kind();
}; // The syntax node of interest, for which we want to check whether
if let Some(kind) = probe.and_then(inbetween_body_and_decl_check) { // it is sandwiched between an item decl signature and its body.
let probe = if ast::ExprStmt::can_cast(p_kind) {
Some(p)
} else if ast::StmtList::can_cast(p_kind) {
Some(it.syntax().clone())
} else {
None
};
if let Some(kind) = probe.and_then(inbetween_body_and_decl_check) {
return Some(make_res(NameRefKind::Keyword(kind)));
}
}
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
make_path_kind_expr(it.into())
},
ast::TupleStructPat(it) => {
path_ctx.has_call_parens = true;
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
},
ast::RecordPat(it) => {
path_ctx.has_call_parens = true;
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
},
ast::PathPat(it) => {
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
},
ast::MacroCall(it) => {
kind_macro_call(it)?
},
ast::Meta(meta) => make_path_kind_attr(meta)?,
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
ast::UseTree(_) => PathKind::Use,
// completing inside a qualifier
ast::Path(parent) => {
path_ctx.parent = Some(parent.clone());
let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?;
match_ast! {
match parent {
ast::PathType(it) => make_path_kind_type(it.into()),
ast::PathExpr(it) => {
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
make_path_kind_expr(it.into())
},
ast::TupleStructPat(it) => {
path_ctx.has_call_parens = true;
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
},
ast::RecordPat(it) => {
path_ctx.has_call_parens = true;
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
},
ast::PathPat(it) => {
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
},
ast::MacroCall(it) => {
kind_macro_call(it)?
},
ast::Meta(meta) => make_path_kind_attr(meta)?,
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
ast::UseTree(_) => PathKind::Use,
ast::RecordExpr(it) => make_path_kind_expr(it.into()),
_ => return None,
}
}
},
ast::RecordExpr(it) => {
// A record expression in this position is usually a result of parsing recovery, so check that
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
return Some(make_res(NameRefKind::Keyword(kind))); return Some(make_res(NameRefKind::Keyword(kind)));
} }
} make_path_kind_expr(it.into())
},
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); _ => return None,
}
make_path_kind_expr(it.into())
},
ast::TupleStructPat(it) => {
path_ctx.has_call_parens = true;
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
},
ast::RecordPat(it) => {
path_ctx.has_call_parens = true;
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
},
ast::PathPat(it) => {
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
},
ast::MacroCall(it) => {
// A macro call in this position is usually a result of parsing recovery, so check that
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
return Some(make_res(NameRefKind::Keyword(kind)));
}
kind_macro_call(it)?
},
ast::Meta(meta) => make_path_kind_attr(meta)?,
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
ast::UseTree(_) => PathKind::Use,
// completing inside a qualifier
ast::Path(parent) => {
path_ctx.parent = Some(parent.clone());
let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?;
match_ast! {
match parent {
ast::PathType(it) => make_path_kind_type(it.into()),
ast::PathExpr(it) => {
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
make_path_kind_expr(it.into())
},
ast::TupleStructPat(it) => {
path_ctx.has_call_parens = true;
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
},
ast::RecordPat(it) => {
path_ctx.has_call_parens = true;
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
},
ast::PathPat(it) => {
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
},
ast::MacroCall(it) => {
kind_macro_call(it)?
},
ast::Meta(meta) => make_path_kind_attr(meta)?,
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
ast::UseTree(_) => PathKind::Use,
ast::RecordExpr(it) => make_path_kind_expr(it.into()),
_ => return None,
}
}
},
ast::RecordExpr(it) => {
// A record expression in this position is usually a result of parsing recovery, so check that
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
return Some(make_res(NameRefKind::Keyword(kind)));
}
make_path_kind_expr(it.into())
},
_ => return None,
} }
}; };
@ -1320,9 +1472,7 @@ fn classify_name_ref(
} }
}) })
} }
PathKind::Item { .. } => { PathKind::Item { .. } => parent.ancestors().find(|it| it.kind() == SyntaxKind::ERROR),
parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind()))
}
_ => None, _ => None,
}; };
if let Some(top) = top_node { if let Some(top) = top_node {

View file

@ -10,7 +10,7 @@ use ide_db::{
}; };
use itertools::Itertools; use itertools::Itertools;
use smallvec::SmallVec; use smallvec::SmallVec;
use stdx::{impl_from, never}; use stdx::{format_to, impl_from, never};
use syntax::{format_smolstr, Edition, SmolStr, TextRange, TextSize}; use syntax::{format_smolstr, Edition, SmolStr, TextRange, TextSize};
use crate::{ use crate::{
@ -27,10 +27,7 @@ use crate::{
#[non_exhaustive] #[non_exhaustive]
pub struct CompletionItem { pub struct CompletionItem {
/// Label in the completion pop up which identifies completion. /// Label in the completion pop up which identifies completion.
pub label: SmolStr, pub label: CompletionItemLabel,
/// Additional label details in the completion pop up that are
/// displayed and aligned on the right side after the label.
pub label_detail: Option<SmolStr>,
/// Range of identifier that is being completed. /// Range of identifier that is being completed.
/// ///
@ -89,11 +86,23 @@ pub struct CompletionItem {
pub import_to_add: SmallVec<[(String, String); 1]>, pub import_to_add: SmallVec<[(String, String); 1]>,
} }
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct CompletionItemLabel {
/// The primary label for the completion item.
pub primary: SmolStr,
/// The left detail for the completion item, usually rendered right next to the primary label.
pub detail_left: Option<String>,
/// The right detail for the completion item, usually rendered right aligned at the end of the completion item.
pub detail_right: Option<String>,
}
// We use custom debug for CompletionItem to make snapshot tests more readable. // We use custom debug for CompletionItem to make snapshot tests more readable.
impl fmt::Debug for CompletionItem { impl fmt::Debug for CompletionItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("CompletionItem"); let mut s = f.debug_struct("CompletionItem");
s.field("label", &self.label).field("source_range", &self.source_range); s.field("label", &self.label.primary)
.field("detail_left", &self.label.detail_left)
.field("detail_right", &self.label.detail_right)
.field("source_range", &self.source_range);
if self.text_edit.len() == 1 { if self.text_edit.len() == 1 {
let atom = self.text_edit.iter().next().unwrap(); let atom = self.text_edit.iter().next().unwrap();
s.field("delete", &atom.delete); s.field("delete", &atom.delete);
@ -102,7 +111,7 @@ impl fmt::Debug for CompletionItem {
s.field("text_edit", &self.text_edit); s.field("text_edit", &self.text_edit);
} }
s.field("kind", &self.kind); s.field("kind", &self.kind);
if self.lookup() != self.label { if self.lookup() != self.label.primary {
s.field("lookup", &self.lookup()); s.field("lookup", &self.lookup());
} }
if let Some(detail) = &self.detail { if let Some(detail) = &self.detail {
@ -434,7 +443,7 @@ impl CompletionItem {
self.ref_match.map(|(mutability, offset)| { self.ref_match.map(|(mutability, offset)| {
( (
format!("&{}{}", mutability.as_keyword_for_ref(), self.label), format!("&{}{}", mutability.as_keyword_for_ref(), self.label.primary),
ide_db::text_edit::Indel::insert( ide_db::text_edit::Indel::insert(
offset, offset,
format!("&{}", mutability.as_keyword_for_ref()), format!("&{}", mutability.as_keyword_for_ref()),
@ -488,13 +497,13 @@ impl Builder {
let _p = tracing::info_span!("item::Builder::build").entered(); let _p = tracing::info_span!("item::Builder::build").entered();
let label = self.label; let label = self.label;
let mut label_detail = None;
let mut lookup = self.lookup.unwrap_or_else(|| label.clone()); let mut lookup = self.lookup.unwrap_or_else(|| label.clone());
let insert_text = self.insert_text.unwrap_or_else(|| label.to_string()); let insert_text = self.insert_text.unwrap_or_else(|| label.to_string());
let mut detail_left = None;
if !self.doc_aliases.is_empty() { if !self.doc_aliases.is_empty() {
let doc_aliases = self.doc_aliases.iter().join(", "); let doc_aliases = self.doc_aliases.iter().join(", ");
label_detail.replace(format_smolstr!(" (alias {doc_aliases})")); detail_left = Some(format!("(alias {doc_aliases})"));
let lookup_doc_aliases = self let lookup_doc_aliases = self
.doc_aliases .doc_aliases
.iter() .iter()
@ -516,16 +525,20 @@ impl Builder {
} }
if let [import_edit] = &*self.imports_to_add { if let [import_edit] = &*self.imports_to_add {
// snippets can have multiple imports, but normal completions only have up to one // snippets can have multiple imports, but normal completions only have up to one
label_detail.replace(format_smolstr!( let detail_left = detail_left.get_or_insert_with(String::new);
"{} (use {})", format_to!(
label_detail.as_deref().unwrap_or_default(), detail_left,
"{}(use {})",
if detail_left.is_empty() { "" } else { " " },
import_edit.import_path.display(db, self.edition) import_edit.import_path.display(db, self.edition)
)); );
} else if let Some(trait_name) = self.trait_name { } else if let Some(trait_name) = self.trait_name {
label_detail.replace(format_smolstr!( let detail_left = detail_left.get_or_insert_with(String::new);
"{} (as {trait_name})", format_to!(
label_detail.as_deref().unwrap_or_default(), detail_left,
)); "{}(as {trait_name})",
if detail_left.is_empty() { "" } else { " " },
);
} }
let text_edit = match self.text_edit { let text_edit = match self.text_edit {
@ -546,8 +559,11 @@ impl Builder {
CompletionItem { CompletionItem {
source_range: self.source_range, source_range: self.source_range,
label, label: CompletionItemLabel {
label_detail, primary: label,
detail_left,
detail_right: self.detail.clone(),
},
text_edit, text_edit,
is_snippet: self.is_snippet, is_snippet: self.is_snippet,
detail: self.detail, detail: self.detail,

View file

@ -748,9 +748,9 @@ mod tests {
let tag = it.kind.tag(); let tag = it.kind.tag();
let relevance = display_relevance(it.relevance); let relevance = display_relevance(it.relevance);
items.push(format!( items.push(format!(
"{tag} {}{} {relevance}\n", "{tag} {} {} {relevance}\n",
it.label, it.label.primary,
it.label_detail.clone().unwrap_or_default(), it.label.detail_right.clone().unwrap_or_default(),
)); ));
if let Some((label, _indel, relevance)) = it.ref_match() { if let Some((label, _indel, relevance)) = it.ref_match() {
@ -812,13 +812,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
st dep::test_mod_b::Struct {} [type_could_unify] st dep::test_mod_b::Struct {} dep::test_mod_b::Struct { } [type_could_unify]
ex dep::test_mod_b::Struct { } [type_could_unify] ex dep::test_mod_b::Struct { } [type_could_unify]
st Struct (use dep::test_mod_b::Struct) [type_could_unify+requires_import] st Struct Struct [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(Struct) []
md dep [] md dep []
st Struct (use dep::test_mod_a::Struct) [requires_import] st Struct Struct [requires_import]
"#]], "#]],
); );
} }
@ -852,11 +852,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
un Union (use dep::test_mod_b::Union) [type_could_unify+requires_import] un Union Union [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(Union) []
md dep [] md dep []
en Union (use dep::test_mod_a::Union) [requires_import] en Union Union [requires_import]
"#]], "#]],
); );
} }
@ -888,13 +888,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ev dep::test_mod_b::Enum::variant [type_could_unify] ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify]
ex dep::test_mod_b::Enum::variant [type_could_unify] ex dep::test_mod_b::Enum::variant [type_could_unify]
en Enum (use dep::test_mod_b::Enum) [type_could_unify+requires_import] en Enum Enum [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(Enum) []
md dep [] md dep []
en Enum (use dep::test_mod_a::Enum) [requires_import] en Enum Enum [requires_import]
"#]], "#]],
); );
} }
@ -926,11 +926,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ev dep::test_mod_b::Enum::Variant [type_could_unify] ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify]
ex dep::test_mod_b::Enum::Variant [type_could_unify] ex dep::test_mod_b::Enum::Variant [type_could_unify]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(Enum) []
md dep [] md dep []
"#]], "#]],
); );
} }
@ -958,11 +958,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn main() [] fn main() fn() []
fn test() [] fn test() fn(fn(usize) -> i32) []
md dep [] md dep []
fn function (use dep::test_mod_a::function) [requires_import] fn function fn(usize) -> i32 [requires_import]
fn function() (use dep::test_mod_b::function) [requires_import] fn function() fn(isize) -> i32 [requires_import]
"#]], "#]],
); );
} }
@ -990,11 +990,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct CONST (use dep::test_mod_b::CONST) [type_could_unify+requires_import] ct CONST i32 [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(i32) []
md dep [] md dep []
ct CONST (use dep::test_mod_a::CONST) [requires_import] ct CONST i64 [requires_import]
"#]], "#]],
); );
} }
@ -1022,11 +1022,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
sc STATIC (use dep::test_mod_b::STATIC) [type_could_unify+requires_import] sc STATIC i32 [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(i32) []
md dep [] md dep []
sc STATIC (use dep::test_mod_a::STATIC) [requires_import] sc STATIC i64 [requires_import]
"#]], "#]],
); );
} }
@ -1058,7 +1058,7 @@ fn main() {
"#, "#,
expect![[r#" expect![[r#"
me Function [] me Function fn(&self, i32) -> bool []
"#]], "#]],
); );
} }
@ -1081,14 +1081,14 @@ fn func(input: Struct) { }
"#, "#,
expect![[r#" expect![[r#"
st Struct [type] st Struct Struct [type]
st Self [type] st Self Self [type]
sp Self [type] sp Self Struct [type]
st Struct [type] st Struct Struct [type]
ex Struct [type] ex Struct [type]
lc self [local] lc self &Struct [local]
fn func() [] fn func() fn(Struct) []
me self.test() [] me self.test() fn(&self) []
"#]], "#]],
); );
} }
@ -1109,13 +1109,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc input [type+name+local] lc input bool [type+name+local]
ex input [type] ex input [type]
ex true [type] ex true [type]
ex false [type] ex false [type]
lc inputbad [local] lc inputbad i32 [local]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(bool) []
"#]], "#]],
); );
} }
@ -1133,6 +1133,10 @@ fn main() { Foo::Fo$0 }
[ [
CompletionItem { CompletionItem {
label: "Foo {…}", label: "Foo {…}",
detail_left: None,
detail_right: Some(
"Foo { x: i32, y: i32 }",
),
source_range: 54..56, source_range: 54..56,
delete: 54..56, delete: 54..56,
insert: "Foo { x: ${1:()}, y: ${2:()} }$0", insert: "Foo { x: ${1:()}, y: ${2:()} }$0",
@ -1161,6 +1165,10 @@ fn main() { Foo::Fo$0 }
[ [
CompletionItem { CompletionItem {
label: "Foo(…)", label: "Foo(…)",
detail_left: None,
detail_right: Some(
"Foo(i32, i32)",
),
source_range: 46..48, source_range: 46..48,
delete: 46..48, delete: 46..48,
insert: "Foo(${1:()}, ${2:()})$0", insert: "Foo(${1:()}, ${2:()})$0",
@ -1189,6 +1197,10 @@ fn main() { fo$0 }
[ [
CompletionItem { CompletionItem {
label: "foo(…)", label: "foo(…)",
detail_left: None,
detail_right: Some(
"fn(u32, u32, T) -> (u32, T)",
),
source_range: 68..70, source_range: 68..70,
delete: 68..70, delete: 68..70,
insert: "foo(${1:a}, ${2:b}, ${3:t})$0", insert: "foo(${1:a}, ${2:b}, ${3:t})$0",
@ -1201,6 +1213,10 @@ fn main() { fo$0 }
}, },
CompletionItem { CompletionItem {
label: "main()", label: "main()",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 68..70, source_range: 68..70,
delete: 68..70, delete: 68..70,
insert: "main();$0", insert: "main();$0",
@ -1228,6 +1244,10 @@ fn main() { Foo::Fo$0 }
[ [
CompletionItem { CompletionItem {
label: "Foo", label: "Foo",
detail_left: None,
detail_right: Some(
"Foo",
),
source_range: 35..37, source_range: 35..37,
delete: 35..37, delete: 35..37,
insert: "Foo$0", insert: "Foo$0",
@ -1260,6 +1280,10 @@ fn main() { let _: m::Spam = S$0 }
[ [
CompletionItem { CompletionItem {
label: "main()", label: "main()",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 75..76, source_range: 75..76,
delete: 75..76, delete: 75..76,
insert: "main();$0", insert: "main();$0",
@ -1271,6 +1295,8 @@ fn main() { let _: m::Spam = S$0 }
}, },
CompletionItem { CompletionItem {
label: "m", label: "m",
detail_left: None,
detail_right: None,
source_range: 75..76, source_range: 75..76,
delete: 75..76, delete: 75..76,
insert: "m", insert: "m",
@ -1280,6 +1306,10 @@ fn main() { let _: m::Spam = S$0 }
}, },
CompletionItem { CompletionItem {
label: "m::Spam::Bar(…)", label: "m::Spam::Bar(…)",
detail_left: None,
detail_right: Some(
"m::Spam::Bar(i32)",
),
source_range: 75..76, source_range: 75..76,
delete: 75..76, delete: 75..76,
insert: "m::Spam::Bar(${1:()})$0", insert: "m::Spam::Bar(${1:()})$0",
@ -1305,6 +1335,10 @@ fn main() { let _: m::Spam = S$0 }
}, },
CompletionItem { CompletionItem {
label: "m::Spam::Foo", label: "m::Spam::Foo",
detail_left: None,
detail_right: Some(
"m::Spam::Foo",
),
source_range: 75..76, source_range: 75..76,
delete: 75..76, delete: 75..76,
insert: "m::Spam::Foo$0", insert: "m::Spam::Foo$0",
@ -1347,6 +1381,10 @@ fn main() { som$0 }
[ [
CompletionItem { CompletionItem {
label: "main()", label: "main()",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 56..59, source_range: 56..59,
delete: 56..59, delete: 56..59,
insert: "main();$0", insert: "main();$0",
@ -1358,6 +1396,10 @@ fn main() { som$0 }
}, },
CompletionItem { CompletionItem {
label: "something_deprecated()", label: "something_deprecated()",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 56..59, source_range: 56..59,
delete: 56..59, delete: 56..59,
insert: "something_deprecated();$0", insert: "something_deprecated();$0",
@ -1382,6 +1424,10 @@ fn foo() { A { the$0 } }
[ [
CompletionItem { CompletionItem {
label: "the_field", label: "the_field",
detail_left: None,
detail_right: Some(
"u32",
),
source_range: 57..60, source_range: 57..60,
delete: 57..60, delete: 57..60,
insert: "the_field", insert: "the_field",
@ -1429,6 +1475,10 @@ impl S {
[ [
CompletionItem { CompletionItem {
label: "bar()", label: "bar()",
detail_left: None,
detail_right: Some(
"fn(self)",
),
source_range: 94..94, source_range: 94..94,
delete: 94..94, delete: 94..94,
insert: "bar();$0", insert: "bar();$0",
@ -1460,6 +1510,10 @@ impl S {
}, },
CompletionItem { CompletionItem {
label: "foo", label: "foo",
detail_left: None,
detail_right: Some(
"{unknown}",
),
source_range: 94..94, source_range: 94..94,
delete: 94..94, delete: 94..94,
insert: "foo", insert: "foo",
@ -1498,6 +1552,8 @@ use self::E::*;
[ [
CompletionItem { CompletionItem {
label: "my", label: "my",
detail_left: None,
detail_right: None,
source_range: 10..12, source_range: 10..12,
delete: 10..12, delete: 10..12,
insert: "my", insert: "my",
@ -1510,6 +1566,10 @@ use self::E::*;
}, },
CompletionItem { CompletionItem {
label: "V", label: "V",
detail_left: None,
detail_right: Some(
"V",
),
source_range: 10..12, source_range: 10..12,
delete: 10..12, delete: 10..12,
insert: "V$0", insert: "V$0",
@ -1524,6 +1584,10 @@ use self::E::*;
}, },
CompletionItem { CompletionItem {
label: "E", label: "E",
detail_left: None,
detail_right: Some(
"E",
),
source_range: 10..12, source_range: 10..12,
delete: 10..12, delete: 10..12,
insert: "E", insert: "E",
@ -1556,6 +1620,10 @@ fn foo(s: S) { s.$0 }
[ [
CompletionItem { CompletionItem {
label: "the_method()", label: "the_method()",
detail_left: None,
detail_right: Some(
"fn(&self)",
),
source_range: 81..81, source_range: 81..81,
delete: 81..81, delete: 81..81,
insert: "the_method();$0", insert: "the_method();$0",
@ -1729,9 +1797,9 @@ fn test(bar: u32) { }
fn foo(s: S) { test(s.$0) } fn foo(s: S) { test(s.$0) }
"#, "#,
expect![[r#" expect![[r#"
fd bar [type+name] fd bar u32 [type+name]
fd baz [type] fd baz u32 [type]
fd foo [] fd foo i64 []
"#]], "#]],
); );
} }
@ -1745,9 +1813,9 @@ struct B { x: (), y: f32, bar: u32 }
fn foo(a: A) { B { bar: a.$0 }; } fn foo(a: A) { B { bar: a.$0 }; }
"#, "#,
expect![[r#" expect![[r#"
fd bar [type+name] fd bar u32 [type+name]
fd baz [type] fd baz u32 [type]
fd foo [] fd foo i64 []
"#]], "#]],
) )
} }
@ -1768,6 +1836,10 @@ fn f() -> i32 {
[ [
CompletionItem { CompletionItem {
label: "0", label: "0",
detail_left: None,
detail_right: Some(
"i32",
),
source_range: 56..57, source_range: 56..57,
delete: 56..57, delete: 56..57,
insert: "0", insert: "0",
@ -1804,9 +1876,9 @@ fn f(foo: i64) { }
fn foo(a: A) { B { bar: f(a.$0) }; } fn foo(a: A) { B { bar: f(a.$0) }; }
"#, "#,
expect![[r#" expect![[r#"
fd foo [type+name] fd foo i64 [type+name]
fd bar [] fd bar u32 []
fd baz [] fd baz u32 []
"#]], "#]],
); );
check_relevance( check_relevance(
@ -1817,9 +1889,9 @@ fn f(foo: i64) { }
fn foo(a: A) { f(B { bar: a.$0 }); } fn foo(a: A) { f(B { bar: a.$0 }); }
"#, "#,
expect![[r#" expect![[r#"
fd bar [type+name] fd bar u32 [type+name]
fd baz [type] fd baz u32 [type]
fd foo [] fd foo i64 []
"#]], "#]],
); );
} }
@ -1832,13 +1904,13 @@ struct WorldSnapshot { _f: () };
fn go(world: &WorldSnapshot) { go(w$0) } fn go(world: &WorldSnapshot) { go(w$0) }
"#, "#,
expect![[r#" expect![[r#"
lc world [type+name+local] lc world &WorldSnapshot [type+name+local]
ex world [type] ex world [type]
st WorldSnapshot {} [] st WorldSnapshot {} WorldSnapshot { _f: () } []
st &WorldSnapshot {} [type] st &WorldSnapshot {} [type]
st WorldSnapshot [] st WorldSnapshot WorldSnapshot []
st &WorldSnapshot [type] st &WorldSnapshot [type]
fn go() [] fn go() fn(&WorldSnapshot) []
"#]], "#]],
); );
} }
@ -1852,9 +1924,9 @@ struct Foo;
fn f(foo: &Foo) { f(foo, w$0) } fn f(foo: &Foo) { f(foo, w$0) }
"#, "#,
expect![[r#" expect![[r#"
lc foo [local] lc foo &Foo [local]
st Foo [] st Foo Foo []
fn f() [] fn f() fn(&Foo) []
"#]], "#]],
); );
} }
@ -1869,12 +1941,12 @@ fn bar() -> u8 { 0 }
fn f() { A { bar: b$0 }; } fn f() { A { bar: b$0 }; }
"#, "#,
expect![[r#" expect![[r#"
fn bar() [type+name] fn bar() fn() -> u8 [type+name]
fn baz() [type] fn baz() fn() -> u8 [type]
ex bar() [type] ex bar() [type]
ex baz() [type] ex baz() [type]
st A [] st A A []
fn f() [] fn f() fn() []
"#]], "#]],
); );
} }
@ -1895,9 +1967,9 @@ fn f() {
} }
"#, "#,
expect![[r#" expect![[r#"
me aaa() [type+name] me aaa() fn(&self) -> u32 [type+name]
me bbb() [type] me bbb() fn(&self) -> u32 [type]
me ccc() [] me ccc() fn(&self) -> u64 []
"#]], "#]],
); );
} }
@ -1916,7 +1988,7 @@ fn f() {
} }
"#, "#,
expect![[r#" expect![[r#"
me aaa() [name] me aaa() fn(&self) -> u64 [name]
"#]], "#]],
); );
} }
@ -1934,14 +2006,14 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc s [name+local] lc s S [name+local]
lc &mut s [type+name+local] lc &mut s [type+name+local]
st S [] st S S []
st &mut S [type] st &mut S [type]
st S [] st S S []
st &mut S [type] st &mut S [type]
fn foo() [] fn foo() fn(&mut S) []
fn main() [] fn main() fn() []
"#]], "#]],
); );
check_relevance( check_relevance(
@ -1954,13 +2026,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc s [type+name+local] lc s S [type+name+local]
st S [type] st S S [type]
st S [type] st S S [type]
ex s [type] ex s [type]
ex S [type] ex S [type]
fn foo() [] fn foo() fn(&mut S) []
fn main() [] fn main() fn() []
"#]], "#]],
); );
check_relevance( check_relevance(
@ -1973,13 +2045,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc ssss [type+local] lc ssss S [type+local]
st S [type] st S S [type]
st S [type] st S S [type]
ex ssss [type] ex ssss [type]
ex S [type] ex S [type]
fn foo() [] fn foo() fn(&mut S) []
fn main() [] fn main() fn() []
"#]], "#]],
); );
} }
@ -2010,19 +2082,19 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify] ex core::ops::Deref::deref(&t) [type_could_unify]
lc m [local] lc m i32 [local]
lc t [local] lc t T [local]
lc &t [type+local] lc &t [type+local]
st S [] st S S []
st &S [type] st &S [type]
st S [] st S S []
st &S [type] st &S [type]
st T [] st T T []
st &T [type] st &T [type]
fn foo() [] fn foo() fn(&S) []
fn main() [] fn main() fn() []
md core [] md core []
"#]], "#]],
) )
} }
@ -2059,19 +2131,19 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify] ex core::ops::DerefMut::deref_mut(&mut t) [type_could_unify]
lc m [local] lc m i32 [local]
lc t [local] lc t T [local]
lc &mut t [type+local] lc &mut t [type+local]
st S [] st S S []
st &mut S [type] st &mut S [type]
st S [] st S S []
st &mut S [type] st &mut S [type]
st T [] st T T []
st &mut T [type] st &mut T [type]
fn foo() [] fn foo() fn(&mut S) []
fn main() [] fn main() fn() []
md core [] md core []
"#]], "#]],
) )
} }
@ -2087,9 +2159,9 @@ fn foo(bar: u32) {
} }
"#, "#,
expect![[r#" expect![[r#"
lc baz [local] lc baz i32 [local]
lc bar [local] lc bar u32 [local]
fn foo() [] fn foo() fn(u32) []
"#]], "#]],
); );
} }
@ -2105,13 +2177,13 @@ fn foo() {
fn bar(t: Foo) {} fn bar(t: Foo) {}
"#, "#,
expect![[r#" expect![[r#"
ev Foo::A [type] ev Foo::A Foo::A [type]
ev Foo::B [type] ev Foo::B Foo::B [type]
en Foo [type] en Foo Foo [type]
ex Foo::A [type] ex Foo::A [type]
ex Foo::B [type] ex Foo::B [type]
fn bar() [] fn bar() fn(Foo) []
fn foo() [] fn foo() fn() []
"#]], "#]],
); );
} }
@ -2127,14 +2199,14 @@ fn foo() {
fn bar(t: &Foo) {} fn bar(t: &Foo) {}
"#, "#,
expect![[r#" expect![[r#"
ev Foo::A [] ev Foo::A Foo::A []
ev &Foo::A [type] ev &Foo::A [type]
ev Foo::B [] ev Foo::B Foo::B []
ev &Foo::B [type] ev &Foo::B [type]
en Foo [] en Foo Foo []
en &Foo [type] en &Foo [type]
fn bar() [] fn bar() fn(&Foo) []
fn foo() [] fn foo() fn() []
"#]], "#]],
); );
} }
@ -2163,18 +2235,18 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ex core::ops::Deref::deref(&bar()) (use core::ops::Deref) [type_could_unify] ex core::ops::Deref::deref(&bar()) [type_could_unify]
st S [] st S S []
st &S [type] st &S [type]
st S [] st S S []
st &S [type] st &S [type]
st T [] st T T []
st &T [type] st &T [type]
fn bar() [] fn bar() fn() -> T []
fn &bar() [type] fn &bar() [type]
fn foo() [] fn foo() fn(&S) []
fn main() [] fn main() fn() []
md core [] md core []
"#]], "#]],
) )
} }
@ -2191,7 +2263,7 @@ impl Sub for u32 {}
fn foo(a: u32) { a.$0 } fn foo(a: u32) { a.$0 }
"#, "#,
expect![[r#" expect![[r#"
me sub() (as Sub) [op_method] me sub() fn(self, Self) -> Self [op_method]
"#]], "#]],
); );
check_relevance( check_relevance(
@ -2212,9 +2284,9 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn new() [] fn new() fn() -> Foo []
me eq() (as PartialEq) [op_method] me eq() fn(&self, &Rhs) -> bool [op_method]
me ne() (as PartialEq) [op_method] me ne() fn(&self, &Rhs) -> bool [op_method]
"#]], "#]],
); );
} }
@ -2238,9 +2310,9 @@ fn test() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn fn_ctr() [type_could_unify] fn fn_ctr() fn() -> Foo [type_could_unify]
fn fn_ctr_self() [type_could_unify] fn fn_ctr_self() fn() -> Option<Foo> [type_could_unify]
fn fn_another() [type_could_unify] fn fn_another() fn(u32) -> Other [type_could_unify]
"#]], "#]],
); );
} }
@ -2384,12 +2456,12 @@ fn test() {
// Constructor // Constructor
// Others // Others
expect![[r#" expect![[r#"
fn fn_direct_ctr() [type_could_unify] fn fn_direct_ctr() fn() -> Foo [type_could_unify]
fn fn_ctr_with_args() [type_could_unify] fn fn_ctr_with_args() fn(u32) -> Foo [type_could_unify]
fn fn_builder() [type_could_unify] fn fn_builder() fn() -> FooBuilder [type_could_unify]
fn fn_ctr() [type_could_unify] fn fn_ctr() fn() -> Result<Foo> [type_could_unify]
me fn_no_ret() [type_could_unify] me fn_no_ret() fn(&self) [type_could_unify]
fn fn_other() [type_could_unify] fn fn_other() fn() -> Result<u32> [type_could_unify]
"#]], "#]],
); );
@ -2420,14 +2492,14 @@ fn test() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn fn_direct_ctr() [type_could_unify] fn fn_direct_ctr() fn() -> Foo<T> [type_could_unify]
fn fn_ctr_with_args() [type_could_unify] fn fn_ctr_with_args() fn(T) -> Foo<T> [type_could_unify]
fn fn_builder() [type_could_unify] fn fn_builder() fn() -> FooBuilder [type_could_unify]
fn fn_ctr_wrapped() [type_could_unify] fn fn_ctr_wrapped() fn() -> Option<Foo<T>> [type_could_unify]
fn fn_ctr_wrapped_2() [type_could_unify] fn fn_ctr_wrapped_2() fn() -> Result<Foo<T>, u32> [type_could_unify]
me fn_returns_unit() [type_could_unify] me fn_returns_unit() fn(&self) [type_could_unify]
fn fn_other() [type_could_unify] fn fn_other() fn() -> Option<u32> [type_could_unify]
"#]], "#]],
); );
} }
@ -2456,13 +2528,13 @@ fn test() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn fn_direct_ctr() [type_could_unify] fn fn_direct_ctr() fn() -> Foo<T> [type_could_unify]
fn fn_ctr_with_args() [type_could_unify] fn fn_ctr_with_args() fn(T) -> Foo<T> [type_could_unify]
fn fn_builder() [type_could_unify] fn fn_builder() fn() -> FooBuilder [type_could_unify]
fn fn_ctr() [type_could_unify] fn fn_ctr() fn() -> Option<Foo<T>> [type_could_unify]
fn fn_ctr2() [type_could_unify] fn fn_ctr2() fn() -> Result<Foo<T>, u32> [type_could_unify]
me fn_no_ret() [type_could_unify] me fn_no_ret() fn(&self) [type_could_unify]
fn fn_other() [type_could_unify] fn fn_other() fn() -> Option<u32> [type_could_unify]
"#]], "#]],
); );
} }
@ -2484,6 +2556,10 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
[ [
CompletionItem { CompletionItem {
label: "baz()", label: "baz()",
detail_left: None,
detail_right: Some(
"fn(&self) -> u32",
),
source_range: 109..110, source_range: 109..110,
delete: 109..110, delete: 109..110,
insert: "baz()$0", insert: "baz()$0",
@ -2513,6 +2589,10 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
}, },
CompletionItem { CompletionItem {
label: "bar", label: "bar",
detail_left: None,
detail_right: Some(
"u32",
),
source_range: 109..110, source_range: 109..110,
delete: 109..110, delete: 109..110,
insert: "bar", insert: "bar",
@ -2524,6 +2604,10 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
}, },
CompletionItem { CompletionItem {
label: "qux", label: "qux",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 109..110, source_range: 109..110,
text_edit: TextEdit { text_edit: TextEdit {
indels: [ indels: [
@ -2562,6 +2646,10 @@ fn foo() {
[ [
CompletionItem { CompletionItem {
label: "field", label: "field",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 76..78, source_range: 76..78,
delete: 76..78, delete: 76..78,
insert: "field", insert: "field",
@ -2610,6 +2698,10 @@ fn main() {
[ [
CompletionItem { CompletionItem {
label: "foo()", label: "foo()",
detail_left: None,
detail_right: Some(
"fn() -> S",
),
source_range: 95..95, source_range: 95..95,
delete: 95..95, delete: 95..95,
insert: "foo()$0", insert: "foo()$0",
@ -2661,15 +2753,15 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc foo [type+local] lc foo Foo<u32> [type+local]
ex foo [type] ex foo [type]
ex Foo::B [type] ex Foo::B [type]
ev Foo::A() [type_could_unify] ev Foo::A() Foo::A(T) [type_could_unify]
ev Foo::B [type_could_unify] ev Foo::B Foo::B [type_could_unify]
en Foo [type_could_unify] en Foo Foo<{unknown}> [type_could_unify]
fn foo() [] fn foo() fn() []
fn bar() [] fn bar() fn() -> Foo<u8> []
fn baz() [] fn baz() fn() -> Foo<T> []
"#]], "#]],
); );
} }
@ -2697,20 +2789,20 @@ fn main() {
"#, "#,
&[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)],
expect![[r#" expect![[r#"
sn not [snippet] sn not !expr [snippet]
me not() (use ops::Not) [type_could_unify+requires_import] me not() fn(self) -> <Self as Not>::Output [type_could_unify+requires_import]
sn if [] sn if if expr {} []
sn while [] sn while while expr {} []
sn ref [] sn ref &expr []
sn refm [] sn refm &mut expr []
sn deref [] sn deref *expr []
sn unsafe [] sn unsafe unsafe {} []
sn match [] sn match match expr {} []
sn box [] sn box Box::new(expr) []
sn dbg [] sn dbg dbg!(expr) []
sn dbgr [] sn dbgr dbg!(&expr) []
sn call [] sn call function(expr) []
sn return [] sn return return expr []
"#]], "#]],
); );
} }
@ -2730,19 +2822,19 @@ fn main() {
"#, "#,
&[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)],
expect![[r#" expect![[r#"
me f() [] me f() fn(&self) []
sn ref [] sn ref &expr []
sn refm [] sn refm &mut expr []
sn deref [] sn deref *expr []
sn unsafe [] sn unsafe unsafe {} []
sn match [] sn match match expr {} []
sn box [] sn box Box::new(expr) []
sn dbg [] sn dbg dbg!(expr) []
sn dbgr [] sn dbgr dbg!(&expr) []
sn call [] sn call function(expr) []
sn let [] sn let let []
sn letm [] sn letm let mut []
sn return [] sn return return expr []
"#]], "#]],
); );
} }
@ -2765,12 +2857,12 @@ fn f() {
} }
"#, "#,
expect![[r#" expect![[r#"
st Buffer [] st Buffer Buffer []
fn f() [] fn f() fn() []
md std [] md std []
tt BufRead (use std::io::BufRead) [requires_import] tt BufRead [requires_import]
st BufReader (use std::io::BufReader) [requires_import] st BufReader BufReader [requires_import]
st BufWriter (use std::io::BufWriter) [requires_import] st BufWriter BufWriter [requires_import]
"#]], "#]],
); );
} }
@ -2979,6 +3071,12 @@ fn main() {
[ [
CompletionItem { CompletionItem {
label: "flush()", label: "flush()",
detail_left: Some(
"(as Write)",
),
detail_right: Some(
"fn(&self)",
),
source_range: 193..193, source_range: 193..193,
delete: 193..193, delete: 193..193,
insert: "flush();$0", insert: "flush();$0",
@ -3006,6 +3104,12 @@ fn main() {
}, },
CompletionItem { CompletionItem {
label: "write()", label: "write()",
detail_left: Some(
"(as Write)",
),
detail_right: Some(
"fn(&self)",
),
source_range: 193..193, source_range: 193..193,
delete: 193..193, delete: 193..193,
insert: "write();$0", insert: "write();$0",

View file

@ -118,10 +118,16 @@ fn completion_list_with_config_raw(
let items = get_all_items(config, ra_fixture, trigger_character); let items = get_all_items(config, ra_fixture, trigger_character);
items items
.into_iter() .into_iter()
.filter(|it| it.kind != CompletionItemKind::BuiltinType || it.label == "u32") .filter(|it| it.kind != CompletionItemKind::BuiltinType || it.label.primary == "u32")
.filter(|it| include_keywords || it.kind != CompletionItemKind::Keyword) .filter(|it| include_keywords || it.kind != CompletionItemKind::Keyword)
.filter(|it| include_keywords || it.kind != CompletionItemKind::Snippet) .filter(|it| include_keywords || it.kind != CompletionItemKind::Snippet)
.sorted_by_key(|it| (it.kind, it.label.clone(), it.detail.as_ref().map(ToOwned::to_owned))) .sorted_by_key(|it| {
(
it.kind,
it.label.primary.clone(),
it.label.detail_left.as_ref().map(ToOwned::to_owned),
)
})
.collect() .collect()
} }
@ -173,27 +179,30 @@ fn render_completion_list(completions: Vec<CompletionItem>) -> String {
let label_width = completions let label_width = completions
.iter() .iter()
.map(|it| { .map(|it| {
monospace_width(&it.label) monospace_width(&it.label.primary)
+ monospace_width(it.label_detail.as_deref().unwrap_or_default()) + monospace_width(it.label.detail_left.as_deref().unwrap_or_default())
+ monospace_width(it.label.detail_right.as_deref().unwrap_or_default())
+ it.label.detail_left.is_some() as usize
+ it.label.detail_right.is_some() as usize
}) })
.max() .max()
.unwrap_or_default() .unwrap_or_default();
.min(22);
completions completions
.into_iter() .into_iter()
.map(|it| { .map(|it| {
let tag = it.kind.tag(); let tag = it.kind.tag();
let var_name = format!("{tag} {}", it.label); let mut buf = format!("{tag} {}", it.label.primary);
let mut buf = var_name; if let Some(label_detail) = &it.label.detail_left {
if let Some(ref label_detail) = it.label_detail { format_to!(buf, " {label_detail}");
format_to!(buf, "{label_detail}");
} }
if let Some(detail) = it.detail { if let Some(detail_right) = it.label.detail_right {
let width = label_width.saturating_sub( let pad_with = label_width.saturating_sub(
monospace_width(&it.label) monospace_width(&it.label.primary)
+ monospace_width(&it.label_detail.unwrap_or_default()), + monospace_width(it.label.detail_left.as_deref().unwrap_or_default())
+ monospace_width(&detail_right)
+ it.label.detail_left.is_some() as usize,
); );
format_to!(buf, "{:width$} {}", "", detail, width = width); format_to!(buf, "{:pad_with$}{detail_right}", "",);
} }
if it.deprecated { if it.deprecated {
format_to!(buf, " DEPRECATED"); format_to!(buf, " DEPRECATED");

View file

@ -33,7 +33,7 @@ pub struct Foo(#[m$0] i32);
at cold at cold
at deny() at deny()
at deprecated at deprecated
at derive macro derive at derive macro derive
at derive() at derive()
at doc = "" at doc = ""
at doc(alias = "") at doc(alias = "")
@ -367,9 +367,9 @@ struct Foo;
at cfg_attr() at cfg_attr()
at deny() at deny()
at deprecated at deprecated
at derive macro derive at derive macro derive
at derive() at derive()
at derive_const macro derive_const at derive_const macro derive_const
at doc = "" at doc = ""
at doc(alias = "") at doc(alias = "")
at doc(hidden) at doc(hidden)
@ -790,10 +790,10 @@ mod derive {
#[derive($0)] struct Test; #[derive($0)] struct Test;
"#, "#,
expect![[r#" expect![[r#"
de Clone macro Clone de Clone macro Clone
de Clone, Copy de Clone, Copy
de Default macro Default de Default macro Default
de PartialEq macro PartialEq de PartialEq macro PartialEq
de PartialEq, Eq de PartialEq, Eq
de PartialEq, Eq, PartialOrd, Ord de PartialEq, Eq, PartialOrd, Ord
de PartialEq, PartialOrd de PartialEq, PartialOrd
@ -812,9 +812,9 @@ mod derive {
#[derive(serde::Serialize, PartialEq, $0)] struct Test; #[derive(serde::Serialize, PartialEq, $0)] struct Test;
"#, "#,
expect![[r#" expect![[r#"
de Clone macro Clone de Clone macro Clone
de Clone, Copy de Clone, Copy
de Default macro Default de Default macro Default
de Eq de Eq
de Eq, PartialOrd, Ord de Eq, PartialOrd, Ord
de PartialOrd de PartialOrd
@ -833,9 +833,9 @@ mod derive {
#[derive($0 serde::Serialize, PartialEq)] struct Test; #[derive($0 serde::Serialize, PartialEq)] struct Test;
"#, "#,
expect![[r#" expect![[r#"
de Clone macro Clone de Clone macro Clone
de Clone, Copy de Clone, Copy
de Default macro Default de Default macro Default
de Eq de Eq
de Eq, PartialOrd, Ord de Eq, PartialOrd, Ord
de PartialOrd de PartialOrd
@ -854,9 +854,9 @@ mod derive {
#[derive(PartialEq, Eq, Or$0)] struct Test; #[derive(PartialEq, Eq, Or$0)] struct Test;
"#, "#,
expect![[r#" expect![[r#"
de Clone macro Clone de Clone macro Clone
de Clone, Copy de Clone, Copy
de Default macro Default de Default macro Default
de PartialOrd de PartialOrd
de PartialOrd, Ord de PartialOrd, Ord
md core md core

View file

@ -26,22 +26,22 @@ fn baz() {
"#, "#,
// This should not contain `FooDesc {…}`. // This should not contain `FooDesc {…}`.
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
en Enum Enum en Enum Enum
fn baz() fn() fn baz() fn()
fn create_foo() fn(&FooDesc) fn create_foo() fn(&FooDesc)
fn function() fn() fn function() fn()
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md _69latrick md _69latrick
md module md module
sc STATIC Unit sc STATIC Unit
st FooDesc FooDesc st FooDesc FooDesc
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
un Union Union un Union Union
ev TupleV() TupleV(u32) ev TupleV() TupleV(u32)
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -76,14 +76,14 @@ fn func(param0 @ (param1, param2): (i32, i32)) {
} }
"#, "#,
expect![[r#" expect![[r#"
fn func() fn((i32, i32)) fn func() fn((i32, i32))
lc ifletlocal i32 lc ifletlocal i32
lc letlocal i32 lc letlocal i32
lc matcharm i32 lc matcharm i32
lc param0 (i32, i32) lc param0 (i32, i32)
lc param1 i32 lc param1 i32
lc param2 i32 lc param2 i32
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -122,25 +122,25 @@ impl Unit {
"#, "#,
// `self` is in here twice, once as the module, once as the local // `self` is in here twice, once as the module, once as the local
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
cp CONST_PARAM cp CONST_PARAM
en Enum Enum en Enum Enum
fn function() fn() fn function() fn()
fn local_func() fn() fn local_func() fn()
me self.foo() fn(self) me self.foo() fn(self)
lc self Unit lc self Unit
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
md qualified md qualified
sp Self Unit sp Self Unit
sc STATIC Unit sc STATIC Unit
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tp TypeParam tp TypeParam
un Union Union un Union Union
ev TupleV() TupleV(u32) ev TupleV() TupleV(u32)
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -187,19 +187,19 @@ impl Unit {
} }
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
en Enum Enum en Enum Enum
fn function() fn() fn function() fn()
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
md qualified md qualified
sc STATIC Unit sc STATIC Unit
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
ev TupleV() TupleV(u32) ev TupleV() TupleV(u32)
?? Unresolved ?? Unresolved
"#]], "#]],
); );
@ -216,8 +216,8 @@ fn complete_in_block() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -264,8 +264,8 @@ fn complete_after_if_expr() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -313,8 +313,8 @@ fn complete_in_match_arm() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -337,8 +337,8 @@ fn completes_in_loop_ctx() {
check_empty( check_empty(
r"fn my() { loop { $0 } }", r"fn my() { loop { $0 } }",
expect![[r#" expect![[r#"
fn my() fn() fn my() fn()
bt u32 u32 bt u32 u32
kw async kw async
kw break kw break
kw const kw const
@ -376,22 +376,22 @@ fn completes_in_loop_ctx() {
check_empty( check_empty(
r"fn my() { loop { foo.$0 } }", r"fn my() { loop { foo.$0 } }",
expect![[r#" expect![[r#"
sn box Box::new(expr) sn box Box::new(expr)
sn break break expr sn break break expr
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn if if expr {} sn if if expr {}
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn not !expr sn not !expr
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
sn while while expr {} sn while while expr {}
"#]], "#]],
); );
} }
@ -401,8 +401,8 @@ fn completes_in_let_initializer() {
check_empty( check_empty(
r#"fn main() { let _ = $0 }"#, r#"fn main() { let _ = $0 }"#,
expect![[r#" expect![[r#"
fn main() fn() fn main() fn()
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -434,9 +434,9 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
st Foo Foo st Foo Foo
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -469,9 +469,9 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
lc bar i32 lc bar i32
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -499,10 +499,10 @@ fn quux(x: i32) {
} }
"#, "#,
expect![[r#" expect![[r#"
fn quux() fn(i32) fn quux() fn(i32)
lc x i32 lc x i32
ma m!() macro_rules! m ma m!() macro_rules! m
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -526,10 +526,10 @@ fn quux(x: i32) {
} }
", ",
expect![[r#" expect![[r#"
fn quux() fn(i32) fn quux() fn(i32)
lc x i32 lc x i32
ma m!() macro_rules! m ma m!() macro_rules! m
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -554,11 +554,11 @@ fn quux(x: i32) {
} }
"#, "#,
expect![[r#" expect![[r#"
fn quux() fn(i32) fn quux() fn(i32)
lc x i32 lc x i32
lc y i32 lc y i32
ma m!() macro_rules! m ma m!() macro_rules! m
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -590,12 +590,12 @@ fn func() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct ASSOC_CONST const ASSOC_CONST: () ct ASSOC_CONST const ASSOC_CONST: ()
fn assoc_fn() fn() fn assoc_fn() fn()
ta AssocType type AssocType = () ta AssocType type AssocType = ()
ev RecordV {} RecordV { field: u32 } ev RecordV {} RecordV { field: u32 }
ev TupleV() TupleV(u32) ev TupleV() TupleV(u32)
ev UnitV UnitV ev UnitV UnitV
"#]], "#]],
); );
} }
@ -633,7 +633,7 @@ fn func() {
"#, "#,
expect![[r#" expect![[r#"
fn variant fn() -> Enum fn variant fn() -> Enum
ev Variant Variant ev Variant Variant
"#]], "#]],
); );
} }
@ -650,8 +650,8 @@ fn main() {
} }
", ",
expect![[r#" expect![[r#"
fn foo() fn() -> impl Trait<U> fn foo() fn() -> impl Trait<U>
fn main() fn() fn main() fn()
tt Trait tt Trait
"#]], "#]],
); );
@ -670,9 +670,9 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn bar() async fn() -> impl Trait<U> fn bar() async fn() -> impl Trait<U>
fn foo() async fn() -> u8 fn foo() async fn() -> u8
fn main() fn() fn main() fn()
tt Trait tt Trait
"#]], "#]],
); );
@ -692,9 +692,9 @@ fn main() {
Foo::$0 Foo::$0
} }
", ",
expect![[r" expect![[r#"
fn bar() fn(impl Trait<U>) fn bar() fn(impl Trait<U>)
"]], "#]],
); );
} }
@ -712,7 +712,7 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn test() fn() -> Zulu fn test() fn() -> Zulu
ex Zulu ex Zulu
ex Zulu::test() ex Zulu::test()
"#]], "#]],
@ -736,11 +736,11 @@ fn brr() {
} }
"#, "#,
expect![[r#" expect![[r#"
en HH HH en HH HH
fn brr() fn() fn brr() fn()
st YoloVariant YoloVariant st YoloVariant YoloVariant
st YoloVariant {} YoloVariant { f: usize } st YoloVariant {} YoloVariant { f: usize }
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -801,8 +801,8 @@ fn else_completion_after_if() {
fn foo() { if foo {} $0 } fn foo() { if foo {} $0 }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -842,8 +842,8 @@ fn foo() { if foo {} $0 }
fn foo() { if foo {} el$0 } fn foo() { if foo {} el$0 }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -883,8 +883,8 @@ fn foo() { if foo {} el$0 }
fn foo() { bar(if foo {} $0) } fn foo() { bar(if foo {} $0) }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw else kw else
kw else if kw else if
@ -907,8 +907,8 @@ fn foo() { bar(if foo {} $0) }
fn foo() { bar(if foo {} el$0) } fn foo() { bar(if foo {} el$0) }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw else kw else
kw else if kw else if
@ -931,8 +931,8 @@ fn foo() { bar(if foo {} el$0) }
fn foo() { if foo {} $0 let x = 92; } fn foo() { if foo {} $0 let x = 92; }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -972,8 +972,8 @@ fn foo() { if foo {} $0 let x = 92; }
fn foo() { if foo {} el$0 let x = 92; } fn foo() { if foo {} el$0 let x = 92; }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -1013,8 +1013,8 @@ fn foo() { if foo {} el$0 let x = 92; }
fn foo() { if foo {} el$0 { let x = 92; } } fn foo() { if foo {} el$0 { let x = 92; } }
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -1065,9 +1065,9 @@ fn main() {
pub struct UnstableThisShouldNotBeListed; pub struct UnstableThisShouldNotBeListed;
"#, "#,
expect![[r#" expect![[r#"
fn main() fn() fn main() fn()
md std md std
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -1117,10 +1117,10 @@ fn main() {
pub struct UnstableButWeAreOnNightlyAnyway; pub struct UnstableButWeAreOnNightlyAnyway;
"#, "#,
expect![[r#" expect![[r#"
fn main() fn() fn main() fn()
md std md std
st UnstableButWeAreOnNightlyAnyway UnstableButWeAreOnNightlyAnyway st UnstableButWeAreOnNightlyAnyway UnstableButWeAreOnNightlyAnyway
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -1170,17 +1170,17 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(&self) me foo() fn(&self)
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
); );
check_empty( check_empty(
@ -1196,17 +1196,17 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(&self) me foo() fn(&self)
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
); );
} }
@ -1226,17 +1226,17 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(&self) me foo() fn(&self)
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
); );
check_empty( check_empty(
@ -1252,17 +1252,17 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(&self) me foo() fn(&self)
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
); );
check_empty( check_empty(
@ -1278,17 +1278,17 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(&self) me foo() fn(&self)
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
); );
check_empty( check_empty(
@ -1304,19 +1304,89 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn if if expr {} sn if if expr {}
sn match match expr {} sn match match expr {}
sn not !expr sn not !expr
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
sn while while expr {} sn while while expr {}
"#]],
);
}
#[test]
fn macro_that_ignores_completion_marker() {
check(
r#"
macro_rules! helper {
($v:ident) => {};
}
macro_rules! m {
($v:ident) => {{
helper!($v);
$v
}};
}
fn main() {
let variable = "test";
m!(v$0);
}
"#,
expect![[r#"
ct CONST Unit
en Enum Enum
fn function() fn()
fn main() fn()
lc variable &str
ma helper!() macro_rules! helper
ma m!() macro_rules! m
ma makro!() macro_rules! makro
md module
sc STATIC Unit
st Record Record
st Tuple Tuple
st Unit Unit
un Union Union
ev TupleV() TupleV(u32)
bt u32 u32
kw async
kw const
kw crate::
kw enum
kw extern
kw false
kw fn
kw for
kw if
kw if let
kw impl
kw let
kw loop
kw match
kw mod
kw self::
kw static
kw struct
kw trait
kw true
kw type
kw union
kw unsafe
kw use
kw while
kw while let
sn macro_rules
sn pd
sn ppd
"#]], "#]],
); );
} }

View file

@ -139,9 +139,9 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
st Rc (use dep::Rc) Rc st Rc (use dep::Rc) Rc
st Rcar (use dep::Rcar) Rcar st Rcar (use dep::Rcar) Rcar
st Rc (use dep::some_module::Rc) Rc st Rc (use dep::some_module::Rc) Rc
st Rcar (use dep::some_module::Rcar) Rcar st Rcar (use dep::some_module::Rcar) Rcar
"#]], "#]],
); );
@ -165,11 +165,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct RC (use dep::RC) () ct RC (use dep::RC) ()
st Rc (use dep::Rc) Rc st Rc (use dep::Rc) Rc
st Rcar (use dep::Rcar) Rcar st Rcar (use dep::Rcar) Rcar
ct RC (use dep::some_module::RC) () ct RC (use dep::some_module::RC) ()
st Rc (use dep::some_module::Rc) Rc st Rc (use dep::some_module::Rc) Rc
st Rcar (use dep::some_module::Rcar) Rcar st Rcar (use dep::some_module::Rcar) Rcar
"#]], "#]],
); );
@ -193,7 +193,7 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct RC (use dep::RC) () ct RC (use dep::RC) ()
ct RC (use dep::some_module::RC) () ct RC (use dep::some_module::RC) ()
"#]], "#]],
); );
@ -227,7 +227,7 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
st ThirdStruct (use dep::some_module::ThirdStruct) ThirdStruct st ThirdStruct (use dep::some_module::ThirdStruct) ThirdStruct
st AfterThirdStruct (use dep::some_module::AfterThirdStruct) AfterThirdStruct st AfterThirdStruct (use dep::some_module::AfterThirdStruct) AfterThirdStruct
st ThiiiiiirdStruct (use dep::some_module::ThiiiiiirdStruct) ThiiiiiirdStruct st ThiiiiiirdStruct (use dep::some_module::ThiiiiiirdStruct) ThiiiiiirdStruct
"#]], "#]],
@ -263,8 +263,8 @@ fn trait_function_fuzzy_completion() {
check( check(
fixture, fixture,
expect![[r#" expect![[r#"
fn weird_function() (use dep::test_mod::TestTrait) fn() fn weird_function() (use dep::test_mod::TestTrait) fn()
"#]], "#]],
); );
check_edit( check_edit(
@ -356,8 +356,8 @@ fn trait_method_fuzzy_completion() {
check( check(
fixture, fixture,
expect![[r#" expect![[r#"
me random_method() (use dep::test_mod::TestTrait) fn(&self) me random_method() (use dep::test_mod::TestTrait) fn(&self)
"#]], "#]],
); );
check_edit( check_edit(
@ -401,8 +401,8 @@ fn main() {
check( check(
fixture, fixture,
expect![[r#" expect![[r#"
me some_method() (use foo::TestTrait) fn(&self) me some_method() (use foo::TestTrait) fn(&self)
"#]], "#]],
); );
check_edit( check_edit(
@ -448,8 +448,8 @@ fn main() {
check( check(
fixture, fixture,
expect![[r#" expect![[r#"
me some_method() (use foo::TestTrait) fn(&self) me some_method() (use foo::TestTrait) fn(&self)
"#]], "#]],
); );
check_edit( check_edit(
@ -496,8 +496,8 @@ fn completion<T: Wrapper>(whatever: T) {
check( check(
fixture, fixture,
expect![[r#" expect![[r#"
me not_in_scope() (use foo::NotInScope) fn(&self) me not_in_scope() (use foo::NotInScope) fn(&self)
"#]], "#]],
); );
check_edit( check_edit(
@ -539,8 +539,8 @@ fn main() {
check( check(
fixture, fixture,
expect![[r#" expect![[r#"
me into() (use test_trait::TestInto) fn(self) -> T me into() (use test_trait::TestInto) fn(self) -> T
"#]], "#]],
); );
} }
@ -568,8 +568,8 @@ fn main() {
check( check(
fixture, fixture,
expect![[r#" expect![[r#"
fn random_method() (use dep::test_mod::TestTrait) fn() fn random_method() (use dep::test_mod::TestTrait) fn()
"#]], "#]],
); );
check_edit( check_edit(
@ -737,8 +737,8 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me random_method() (use dep::test_mod::TestTrait) fn(&self) DEPRECATED me random_method() (use dep::test_mod::TestTrait) fn(&self) DEPRECATED
"#]], "#]],
); );
check( check(
@ -767,8 +767,8 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED
fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED
me random_method() (use dep::test_mod::TestTrait) fn(&self) DEPRECATED me random_method() (use dep::test_mod::TestTrait) fn(&self) DEPRECATED
"#]], "#]],
); );
@ -1117,7 +1117,7 @@ fn main() {
tes$0 tes$0
}"#, }"#,
expect![[r#" expect![[r#"
ct TEST_CONST (use foo::TEST_CONST) usize ct TEST_CONST (use foo::TEST_CONST) usize
fn test_function() (use foo::test_function) fn() -> i32 fn test_function() (use foo::test_function) fn() -> i32
"#]], "#]],
); );
@ -1175,8 +1175,8 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn some_fn() (use m::some_fn) fn() -> i32 fn some_fn() (use m::some_fn) fn() -> i32
"#]], "#]],
); );
} }
@ -1691,7 +1691,7 @@ fn function() {
expect![[r#" expect![[r#"
st FooStruct (use outer::FooStruct) BarStruct st FooStruct (use outer::FooStruct) BarStruct
md foo (use outer::foo) md foo (use outer::foo)
fn foo_fun() (use outer::foo_fun) fn() fn foo_fun() (use outer::foo_fun) fn()
"#]], "#]],
); );
} }
@ -1720,3 +1720,45 @@ fn function() {
"#]], "#]],
); );
} }
#[test]
fn intrinsics() {
check(
r#"
//- /core.rs crate:core
pub mod intrinsics {
extern "rust-intrinsic" {
pub fn transmute<Src, Dst>(src: Src) -> Dst;
}
}
pub mod mem {
pub use crate::intrinsics::transmute;
}
//- /main.rs crate:main deps:core
fn function() {
transmute$0
}
"#,
expect![[r#"
fn transmute() (use core::mem::transmute) unsafe fn(Src) -> Dst
"#]],
);
check(
r#"
//- /core.rs crate:core
pub mod intrinsics {
extern "rust-intrinsic" {
pub fn transmute<Src, Dst>(src: Src) -> Dst;
}
}
pub mod mem {
pub use crate::intrinsics::transmute;
}
//- /main.rs crate:main deps:core
fn function() {
mem::transmute$0
}
"#,
expect![""],
);
}

View file

@ -140,7 +140,7 @@ fn foo2($0) {}
expect![[r#" expect![[r#"
st Bar st Bar
bn Bar { bar }: Bar bn Bar { bar }: Bar
bn Bar {} Bar { bar$1 }: Bar$0 bn Bar {} Bar { bar$1 }: Bar$0
kw mut kw mut
kw ref kw ref
"#]], "#]],

View file

@ -20,15 +20,15 @@ fn target_type_or_trait_in_impl_block() {
impl Tra$0 impl Tra$0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -42,15 +42,15 @@ fn target_type_in_trait_impl_block() {
impl Trait for Str$0 impl Trait for Str$0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],

View file

@ -13,7 +13,7 @@ fn in_mod_item_list() {
check( check(
r#"mod tests { $0 }"#, r#"mod tests { $0 }"#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -46,7 +46,7 @@ fn in_source_file_item_list() {
check( check(
r#"$0"#, r#"$0"#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
kw async kw async
kw const kw const
@ -79,7 +79,7 @@ fn in_item_list_after_attr() {
check( check(
r#"#[attr] $0"#, r#"#[attr] $0"#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
kw async kw async
kw const kw const
@ -182,7 +182,7 @@ fn in_impl_assoc_item_list() {
check( check(
r#"impl Struct { $0 }"#, r#"impl Struct { $0 }"#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
kw async kw async
kw const kw const
@ -202,7 +202,7 @@ fn in_impl_assoc_item_list_after_attr() {
check( check(
r#"impl Struct { #[attr] $0 }"#, r#"impl Struct { #[attr] $0 }"#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
kw async kw async
kw const kw const
@ -315,7 +315,7 @@ impl Test for () {
fn async fn function2() fn async fn function2()
fn fn function1() fn fn function1()
fn fn function2() fn fn function2()
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
ta type Type1 = ta type Type1 =
kw crate:: kw crate::
@ -381,7 +381,7 @@ fn after_unit_struct() {
check( check(
r#"struct S; f$0"#, r#"struct S; f$0"#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
kw async kw async
kw const kw const
@ -503,7 +503,7 @@ fn inside_extern_blocks() {
check( check(
r#"extern { $0 }"#, r#"extern { $0 }"#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
kw crate:: kw crate::
kw fn kw fn
@ -520,7 +520,7 @@ fn inside_extern_blocks() {
check( check(
r#"unsafe extern { $0 }"#, r#"unsafe extern { $0 }"#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
kw crate:: kw crate::
kw fn kw fn

View file

@ -122,15 +122,15 @@ fn foo() {
expect![[r#" expect![[r#"
ct CONST ct CONST
en Enum en Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record st Record
st Tuple st Tuple
st Unit st Unit
ev TupleV ev TupleV
bn Record {} Record { field$1 }$0 bn Record {} Record { field$1 }$0
bn Tuple() Tuple($1)$0 bn Tuple() Tuple($1)$0
bn TupleV() TupleV($1)$0 bn TupleV() TupleV($1)$0
kw mut kw mut
kw ref kw ref
"#]], "#]],
@ -151,15 +151,15 @@ fn foo() {
"#, "#,
expect![[r#" expect![[r#"
en SingleVariantEnum en SingleVariantEnum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record st Record
st Tuple st Tuple
st Unit st Unit
ev Variant ev Variant
bn Record {} Record { field$1 }$0 bn Record {} Record { field$1 }$0
bn Tuple() Tuple($1)$0 bn Tuple() Tuple($1)$0
bn Variant Variant$0 bn Variant Variant$0
kw mut kw mut
kw ref kw ref
"#]], "#]],
@ -174,13 +174,13 @@ fn foo(a$0) {
} }
"#, "#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record st Record
st Tuple st Tuple
st Unit st Unit
bn Record {} Record { field$1 }: Record$0 bn Record {} Record { field$1 }: Record$0
bn Tuple() Tuple($1): Tuple$0 bn Tuple() Tuple($1): Tuple$0
kw mut kw mut
kw ref kw ref
"#]], "#]],
@ -191,13 +191,13 @@ fn foo(a$0: Tuple) {
} }
"#, "#,
expect![[r#" expect![[r#"
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record st Record
st Tuple st Tuple
st Unit st Unit
bn Record {} Record { field$1 }$0 bn Record {} Record { field$1 }$0
bn Tuple() Tuple($1)$0 bn Tuple() Tuple($1)$0
bn tuple bn tuple
kw mut kw mut
kw ref kw ref
@ -240,7 +240,7 @@ fn foo() {
expect![[r#" expect![[r#"
en E en E
ma m!() macro_rules! m ma m!() macro_rules! m
bn E::X E::X$0 bn E::X E::X$0
kw mut kw mut
kw ref kw ref
"#]], "#]],
@ -268,7 +268,7 @@ fn outer() {
st Record st Record
st Tuple st Tuple
bn Record {} Record { field$1, .. }$0 bn Record {} Record { field$1, .. }$0
bn Tuple() Tuple($1, ..)$0 bn Tuple() Tuple($1, ..)$0
kw mut kw mut
kw ref kw ref
"#]], "#]],
@ -291,7 +291,7 @@ impl Foo {
expect![[r#" expect![[r#"
sp Self sp Self
st Foo st Foo
bn Foo() Foo($1)$0 bn Foo() Foo($1)$0
bn Self() Self($1)$0 bn Self() Self($1)$0
kw mut kw mut
kw ref kw ref
@ -315,8 +315,8 @@ fn func() {
expect![[r#" expect![[r#"
ct ASSOC_CONST const ASSOC_CONST: () ct ASSOC_CONST const ASSOC_CONST: ()
bn RecordV {} RecordV { field$1 }$0 bn RecordV {} RecordV { field$1 }$0
bn TupleV() TupleV($1)$0 bn TupleV() TupleV($1)$0
bn UnitV UnitV$0 bn UnitV UnitV$0
"#]], "#]],
); );
} }
@ -332,7 +332,7 @@ fn outer(Foo { bar: $0 }: Foo) {}
expect![[r#" expect![[r#"
st Bar st Bar
st Foo st Foo
bn Bar() Bar($1)$0 bn Bar() Bar($1)$0
bn Foo {} Foo { bar$1 }$0 bn Foo {} Foo { bar$1 }$0
kw mut kw mut
kw ref kw ref
@ -395,7 +395,7 @@ fn foo($0) {}
expect![[r#" expect![[r#"
st Bar st Bar
st Foo st Foo
bn Bar() Bar($1): Bar$0 bn Bar() Bar($1): Bar$0
bn Foo {} Foo { bar$1 }: Foo$0 bn Foo {} Foo { bar$1 }: Foo$0
kw mut kw mut
kw ref kw ref
@ -416,7 +416,7 @@ fn foo() {
expect![[r#" expect![[r#"
st Bar st Bar
st Foo st Foo
bn Bar() Bar($1)$0 bn Bar() Bar($1)$0
bn Foo {} Foo { bar$1 }$0 bn Foo {} Foo { bar$1 }$0
kw mut kw mut
kw ref kw ref
@ -436,7 +436,7 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
st Bar Bar st Bar Bar
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -451,7 +451,7 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
st Foo Foo st Foo Foo
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -535,10 +535,10 @@ fn foo() {
"#, "#,
expect![[r#" expect![[r#"
en Enum en Enum
bn Enum::A Enum::A$0 bn Enum::A Enum::A$0
bn Enum::B {} Enum::B { r#type$1 }$0 bn Enum::B {} Enum::B { r#type$1 }$0
bn Enum::struct {} Enum::r#struct { r#type$1 }$0 bn Enum::struct {} Enum::r#struct { r#type$1 }$0
bn Enum::type Enum::r#type$0 bn Enum::type Enum::r#type$0
kw mut kw mut
kw ref kw ref
"#]], "#]],
@ -559,10 +559,10 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
bn A A$0 bn A A$0
bn B {} B { r#type$1 }$0 bn B {} B { r#type$1 }$0
bn struct {} r#struct { r#type$1 }$0 bn struct {} r#struct { r#type$1 }$0
bn type r#type$0 bn type r#type$0
"#]], "#]],
); );
} }
@ -672,8 +672,8 @@ impl Ty {
st Ty st Ty
bn &mut self bn &mut self
bn &self bn &self
bn Self() Self($1): Self$0 bn Self() Self($1): Self$0
bn Ty() Ty($1): Ty$0 bn Ty() Ty($1): Ty$0
bn mut self bn mut self
bn self bn self
kw mut kw mut
@ -693,8 +693,8 @@ impl Ty {
st Ty st Ty
bn &mut self bn &mut self
bn &self bn &self
bn Self() Self($1): Self$0 bn Self() Self($1): Self$0
bn Ty() Ty($1): Ty$0 bn Ty() Ty($1): Ty$0
bn mut self bn mut self
bn self bn self
kw mut kw mut
@ -714,8 +714,8 @@ impl Ty {
st Ty st Ty
bn &mut self bn &mut self
bn &self bn &self
bn Self() Self($1): Self$0 bn Self() Self($1): Self$0
bn Ty() Ty($1): Ty$0 bn Ty() Ty($1): Ty$0
bn mut self bn mut self
bn self bn self
kw mut kw mut
@ -734,7 +734,7 @@ impl Ty {
sp Self sp Self
st Ty st Ty
bn Self() Self($1): Self$0 bn Self() Self($1): Self$0
bn Ty() Ty($1): Ty$0 bn Ty() Ty($1): Ty$0
kw mut kw mut
kw ref kw ref
"#]], "#]],
@ -763,7 +763,7 @@ fn f(x: EnumAlias<u8>) {
"#, "#,
expect![[r#" expect![[r#"
bn Tuple() Tuple($1)$0 bn Tuple() Tuple($1)$0
bn Unit Unit$0 bn Unit Unit$0
"#]], "#]],
); );
} }

View file

@ -16,16 +16,16 @@ fn predicate_start() {
struct Foo<'lt, T, const C: usize> where $0 {} struct Foo<'lt, T, const C: usize> where $0 {}
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo<> Foo<'_, {unknown}, _> st Foo<> Foo<'_, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -89,16 +89,16 @@ fn param_list_for_for_pred() {
struct Foo<'lt, T, const C: usize> where for<'a> $0 {} struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo<> Foo<'_, {unknown}, _> st Foo<> Foo<'_, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -114,16 +114,16 @@ impl Record {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
sp Self Record sp Self Record
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],

View file

@ -24,19 +24,19 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(&self) me foo() fn(&self)
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
) )
} }
@ -57,19 +57,19 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(&self) me foo() fn(&self)
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
) )
} }
@ -92,19 +92,19 @@ impl Foo {
fn main() {} fn main() {}
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(&self) me foo() fn(&self)
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
) )
} }
@ -127,19 +127,19 @@ impl Foo {
fn main() {} fn main() {}
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(&self) me foo() fn(&self)
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
) )
} }

View file

@ -70,8 +70,8 @@ fn foo(baz: Baz) {
ev Ok ev Ok
bn Baz::Bar Baz::Bar$0 bn Baz::Bar Baz::Bar$0
bn Baz::Foo Baz::Foo$0 bn Baz::Foo Baz::Foo$0
bn Err() Err($1)$0 bn Err() Err($1)$0
bn Ok() Ok($1)$0 bn Ok() Ok($1)$0
kw mut kw mut
kw ref kw ref
"#]], "#]],
@ -91,20 +91,20 @@ fn foo(baz: Baz) {
} }
"#, "#,
expect![[r#" expect![[r#"
en Baz en Baz
en Result en Result
md core md core
ev Bar ev Bar
ev Err ev Err
ev Foo ev Foo
ev Ok ev Ok
bn Bar Bar$0 bn Bar Bar$0
bn Err() Err($1)$0 bn Err() Err($1)$0
bn Foo Foo$0 bn Foo Foo$0
bn Ok() Ok($1)$0 bn Ok() Ok($1)$0
kw mut kw mut
kw ref kw ref
"#]], "#]],
); );
} }
@ -184,14 +184,14 @@ fn main() {
"#, "#,
expect![[r#" expect![[r#"
fd ..Default::default() fd ..Default::default()
fn main() fn() fn main() fn()
lc foo Foo lc foo Foo
lc thing i32 lc thing i32
md core md core
st Foo Foo st Foo Foo
st Foo {} Foo { foo1: u32, foo2: u32 } st Foo {} Foo { foo1: u32, foo2: u32 }
tt Default tt Default
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
ex Foo::default() ex Foo::default()
@ -238,8 +238,8 @@ fn main() {
"#, "#,
expect![[r#" expect![[r#"
fd ..Default::default() fd ..Default::default()
fd foo1 u32 fd foo1 u32
fd foo2 u32 fd foo2 u32
"#]], "#]],
); );
} }

View file

@ -87,7 +87,7 @@ pub mod prelude {
expect![[r#" expect![[r#"
md std md std
st Option Option st Option Option
bt u32 u32 bt u32 u32
"#]], "#]],
); );
} }
@ -113,10 +113,10 @@ mod macros {
} }
"#, "#,
expect![[r#" expect![[r#"
fn f() fn() fn f() fn()
ma concat!() macro_rules! concat ma concat!() macro_rules! concat
md std md std
bt u32 u32 bt u32 u32
"#]], "#]],
); );
} }
@ -146,7 +146,7 @@ pub mod prelude {
md core md core
md std md std
st String String st String String
bt u32 u32 bt u32 u32
"#]], "#]],
); );
} }
@ -174,7 +174,7 @@ pub mod prelude {
expect![[r#" expect![[r#"
fn f() fn() fn f() fn()
md std md std
bt u32 u32 bt u32 u32
"#]], "#]],
); );
} }
@ -226,9 +226,9 @@ impl S {
fn foo() { let _ = lib::S::$0 } fn foo() { let _ = lib::S::$0 }
"#, "#,
expect![[r#" expect![[r#"
ct PUBLIC_CONST pub const PUBLIC_CONST: u32 ct PUBLIC_CONST pub const PUBLIC_CONST: u32
fn public_method() fn() fn public_method() fn()
ta PublicType pub type PublicType = u32 ta PublicType pub type PublicType = u32
"#]], "#]],
); );
} }
@ -317,14 +317,14 @@ trait Sub: Super {
fn foo<T: Sub>() { T::$0 } fn foo<T: Sub>() { T::$0 }
"#, "#,
expect![[r#" expect![[r#"
ct C2 (as Sub) const C2: () ct C2 (as Sub) const C2: ()
ct CONST (as Super) const CONST: u8 ct CONST (as Super) const CONST: u8
fn func() (as Super) fn() fn func() (as Super) fn()
fn subfunc() (as Sub) fn() fn subfunc() (as Sub) fn()
me method() (as Super) fn(&self) me method() (as Super) fn(&self)
me submethod() (as Sub) fn(&self) me submethod() (as Sub) fn(&self)
ta SubTy (as Sub) type SubTy ta SubTy (as Sub) type SubTy
ta Ty (as Super) type Ty ta Ty (as Super) type Ty
"#]], "#]],
); );
} }
@ -357,14 +357,14 @@ impl<T> Sub for Wrap<T> {
} }
"#, "#,
expect![[r#" expect![[r#"
ct C2 (as Sub) const C2: () ct C2 (as Sub) const C2: ()
ct CONST (as Super) const CONST: u8 ct CONST (as Super) const CONST: u8
fn func() (as Super) fn() fn func() (as Super) fn()
fn subfunc() (as Sub) fn() fn subfunc() (as Sub) fn()
me method() (as Super) fn(&self) me method() (as Super) fn(&self)
me submethod() (as Sub) fn(&self) me submethod() (as Sub) fn(&self)
ta SubTy (as Sub) type SubTy ta SubTy (as Sub) type SubTy
ta Ty (as Super) type Ty ta Ty (as Super) type Ty
"#]], "#]],
); );
} }
@ -381,9 +381,9 @@ impl T { fn bar() {} }
fn main() { T::$0; } fn main() { T::$0; }
"#, "#,
expect![[r#" expect![[r#"
fn bar() fn() fn bar() fn()
fn foo() fn() fn foo() fn()
"#]], "#]],
); );
} }
@ -397,7 +397,7 @@ macro_rules! foo { () => {} }
fn main() { let _ = crate::$0 } fn main() { let _ = crate::$0 }
"#, "#,
expect![[r#" expect![[r#"
fn main() fn() fn main() fn()
ma foo!() macro_rules! foo ma foo!() macro_rules! foo
"#]], "#]],
); );
@ -447,9 +447,9 @@ mod p {
} }
"#, "#,
expect![[r#" expect![[r#"
ct RIGHT_CONST u32 ct RIGHT_CONST u32
fn right_fn() fn() fn right_fn() fn()
st RightType WrongType st RightType WrongType
"#]], "#]],
); );
@ -495,9 +495,9 @@ fn main() { m!(self::f$0); }
fn foo() {} fn foo() {}
"#, "#,
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
fn main() fn() fn main() fn()
"#]], "#]],
); );
} }
@ -513,9 +513,9 @@ mod m {
} }
"#, "#,
expect![[r#" expect![[r#"
fn z() fn() fn z() fn()
md z md z
"#]], "#]],
); );
} }
@ -534,8 +534,8 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn new() fn() -> HashMap<K, V, RandomState> fn new() fn() -> HashMap<K, V, RandomState>
"#]], "#]],
); );
} }
@ -557,8 +557,8 @@ impl Foo {
"#, "#,
expect![[r#" expect![[r#"
me foo() fn(self) me foo() fn(self)
ev Bar Bar ev Bar Bar
ev Baz Baz ev Baz Baz
"#]], "#]],
); );
} }
@ -578,9 +578,9 @@ fn foo(self) {
} }
"#, "#,
expect![[r#" expect![[r#"
ev Bar Bar ev Bar Bar
ev Baz Baz ev Baz Baz
"#]], "#]],
); );
check_no_kw( check_no_kw(
@ -598,8 +598,8 @@ enum Foo {
} }
"#, "#,
expect![[r#" expect![[r#"
ev Baz Baz ev Baz Baz
"#]], "#]],
); );
} }
@ -623,9 +623,9 @@ impl u8 {
} }
"#, "#,
expect![[r#" expect![[r#"
ct MAX pub const MAX: Self ct MAX pub const MAX: Self
me func() fn(self) me func() fn(self)
"#]], "#]],
); );
} }
@ -643,8 +643,8 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ev Bar Bar ev Bar Bar
"#]], "#]],
); );
} }
@ -723,7 +723,7 @@ fn bar() -> Bar {
} }
"#, "#,
expect![[r#" expect![[r#"
fn bar() fn() fn bar() fn()
fn foo() (as Foo) fn() -> Self fn foo() (as Foo) fn() -> Self
ex Bar ex Bar
ex bar() ex bar()
@ -787,7 +787,7 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me by_macro() (as MyTrait) fn(&self) me by_macro() (as MyTrait) fn(&self)
me not_by_macro() (as MyTrait) fn(&self) me not_by_macro() (as MyTrait) fn(&self)
"#]], "#]],
) )
@ -827,7 +827,7 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
me by_macro() (as MyTrait) fn(&self) me by_macro() (as MyTrait) fn(&self)
me not_by_macro() (as MyTrait) fn(&self) me not_by_macro() (as MyTrait) fn(&self)
"#]], "#]],
) )
@ -885,10 +885,10 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn main() fn() fn main() fn()
lc foobar i32 lc foobar i32
ma x!() macro_rules! x ma x!() macro_rules! x
bt u32 u32 bt u32 u32
"#]], "#]],
) )
} }
@ -1014,7 +1014,7 @@ fn here_we_go() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn here_we_go() fn() fn here_we_go() fn()
st Foo (alias Bar) Foo st Foo (alias Bar) Foo
bt u32 u32 bt u32 u32
kw async kw async
@ -1064,9 +1064,9 @@ fn here_we_go() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn here_we_go() fn() fn here_we_go() fn()
st Foo (alias Bar, Qux, Baz) Foo st Foo (alias Bar, Qux, Baz) Foo
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -1160,20 +1160,20 @@ fn here_we_go() {
} }
"#, "#,
expect![[r#" expect![[r#"
fd bar u8 fd bar u8
me baz() (alias qux) fn(&self) -> u8 me baz() (alias qux) fn(&self) -> u8
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
sn dbgr dbg!(&expr) sn dbgr dbg!(&expr)
sn deref *expr sn deref *expr
sn let let sn let let
sn letm let mut sn letm let mut
sn match match expr {} sn match match expr {}
sn ref &expr sn ref &expr
sn refm &mut expr sn refm &mut expr
sn return return expr sn return return expr
sn unsafe unsafe {} sn unsafe unsafe {}
"#]], "#]],
); );
} }
@ -1189,7 +1189,7 @@ fn bar() { qu$0 }
expect![[r#" expect![[r#"
fn bar() fn() fn bar() fn()
fn foo() (alias qux) fn() fn foo() (alias qux) fn()
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -1277,10 +1277,10 @@ fn here_we_go() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn here_we_go() fn() fn here_we_go() fn()
md foo md foo
st Bar (alias Qux) (use foo::Bar) Bar st Bar (alias Qux) (use foo::Bar) Bar
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw false kw false
kw for kw for
@ -1315,10 +1315,9 @@ use krate::e;
fn main() { fn main() {
e::$0 e::$0
}"#, }"#,
expect![ expect![[r#"
"fn i_am_public() fn() fn i_am_public() fn()
" "#]],
],
) )
} }
@ -1444,8 +1443,8 @@ fn foo() {
"#, "#,
Some('_'), Some('_'),
expect![[r#" expect![[r#"
fn foo() fn() fn foo() fn()
bt u32 u32 bt u32 u32
kw async kw async
kw const kw const
kw crate:: kw crate::
@ -1498,7 +1497,7 @@ fn foo(_: a_$0) { }
"#, "#,
Some('_'), Some('_'),
expect![[r#" expect![[r#"
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -1512,7 +1511,7 @@ fn foo<T>() {
Some('_'), Some('_'),
expect![[r#" expect![[r#"
tp T tp T
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],

View file

@ -17,18 +17,18 @@ struct Foo<'lt, T, const C: usize> {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
sp Self Foo<'_, {unknown}, _> sp Self Foo<'_, {unknown}, _>
st Foo<> Foo<'_, {unknown}, _> st Foo<> Foo<'_, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
tp T tp T
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -42,18 +42,18 @@ fn tuple_struct_field() {
struct Foo<'lt, T, const C: usize>(f$0); struct Foo<'lt, T, const C: usize>(f$0);
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
sp Self Foo<'_, {unknown}, _> sp Self Foo<'_, {unknown}, _>
st Foo<> Foo<'_, {unknown}, _> st Foo<> Foo<'_, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
tp T tp T
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw pub kw pub
kw pub(crate) kw pub(crate)
@ -70,16 +70,16 @@ fn fn_return_type() {
fn x<'lt, T, const C: usize>() -> $0 fn x<'lt, T, const C: usize>() -> $0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
tp T tp T
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -100,15 +100,15 @@ fn foo() -> B$0 {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
it () it ()
kw crate:: kw crate::
kw self:: kw self::
@ -124,16 +124,16 @@ struct Foo<T>(T);
const FOO: $0 = Foo(2); const FOO: $0 = Foo(2);
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo<> Foo<{unknown}> st Foo<> Foo<{unknown}>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
it Foo<i32> it Foo<i32>
kw crate:: kw crate::
kw self:: kw self::
@ -151,15 +151,15 @@ fn f2() {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
it i32 it i32
kw crate:: kw crate::
kw self:: kw self::
@ -179,15 +179,15 @@ fn f2() {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
it u64 it u64
kw crate:: kw crate::
kw self:: kw self::
@ -204,15 +204,15 @@ fn f2(x: u64) -> $0 {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
it u64 it u64
kw crate:: kw crate::
kw self:: kw self::
@ -230,15 +230,15 @@ fn f2(x: $0) {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
it i32 it i32
kw crate:: kw crate::
kw self:: kw self::
@ -262,17 +262,17 @@ fn foo<'lt, T, const C: usize>() {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md a md a
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
tp T tp T
un Union Union un Union Union
bt u32 u32 bt u32 u32
it a::Foo<a::Foo<i32>> it a::Foo<a::Foo<i32>>
kw crate:: kw crate::
kw self:: kw self::
@ -291,17 +291,17 @@ fn foo<'lt, T, const C: usize>() {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo<> Foo<{unknown}> st Foo<> Foo<{unknown}>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
tp T tp T
un Union Union un Union Union
bt u32 u32 bt u32 u32
it Foo<i32> it Foo<i32>
kw crate:: kw crate::
kw self:: kw self::
@ -319,16 +319,16 @@ fn foo<'lt, T, const C: usize>() {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
tp T tp T
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -341,14 +341,14 @@ fn foo<'lt, T, const C: usize>() {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
"#]], "#]],
); );
} }
@ -368,7 +368,7 @@ trait Trait2: Trait1 {
fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#, "#,
expect![[r#" expect![[r#"
ta Foo = (as Trait2) type Foo ta Foo = (as Trait2) type Foo
ta Super = (as Trait1) type Super ta Super = (as Trait1) type Super
"#]], "#]],
); );
@ -384,18 +384,18 @@ trait Trait2<T>: Trait1 {
fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
tt Trait1 tt Trait1
tt Trait2 tt Trait2
tp T tp T
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -409,15 +409,15 @@ trait Trait2<T> {
fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {} fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
tt Trait2 tt Trait2
un Union Union un Union Union
"#]], "#]],
); );
} }
@ -434,18 +434,18 @@ trait Tr<T> {
impl Tr<$0 impl Tr<$0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
sp Self dyn Tr<{unknown}> sp Self dyn Tr<{unknown}>
st Record Record st Record Record
st S S st S S
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Tr tt Tr
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -481,16 +481,16 @@ trait MyTrait<T, U> {
fn f(t: impl MyTrait<u$0 fn f(t: impl MyTrait<u$0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt MyTrait tt MyTrait
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -506,16 +506,16 @@ trait MyTrait<T, U> {
fn f(t: impl MyTrait<u8, u$0 fn f(t: impl MyTrait<u8, u$0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt MyTrait tt MyTrait
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -549,16 +549,16 @@ trait MyTrait<T, U = u8> {
fn f(t: impl MyTrait<u$0 fn f(t: impl MyTrait<u$0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt MyTrait tt MyTrait
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -574,18 +574,18 @@ trait MyTrait<T, U = u8> {
fn f(t: impl MyTrait<u8, u$0 fn f(t: impl MyTrait<u8, u$0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt MyTrait tt MyTrait
tt Trait tt Trait
ta Item1 = (as MyTrait) type Item1 ta Item1 = (as MyTrait) type Item1
ta Item2 = (as MyTrait) type Item2 ta Item2 = (as MyTrait) type Item2
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -619,16 +619,16 @@ trait MyTrait {
fn f(t: impl MyTrait<Item1 = $0 fn f(t: impl MyTrait<Item1 = $0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt MyTrait tt MyTrait
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -644,16 +644,16 @@ trait MyTrait {
fn f(t: impl MyTrait<Item1 = u8, Item2 = $0 fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt MyTrait tt MyTrait
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -668,7 +668,7 @@ trait MyTrait {
fn f(t: impl MyTrait<C = $0 fn f(t: impl MyTrait<C = $0
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -691,9 +691,9 @@ pub struct S;
"#, "#,
expect![[r#" expect![[r#"
md std md std
sp Self Foo sp Self Foo
st Foo Foo st Foo Foo
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -716,10 +716,10 @@ pub struct S;
"#, "#,
expect![[r#" expect![[r#"
md std md std
sp Self Foo sp Self Foo
st Foo Foo st Foo Foo
st S S st S S
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -739,16 +739,16 @@ fn completes_const_and_type_generics_separately() {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo Foo st Foo Foo
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -766,8 +766,8 @@ fn completes_const_and_type_generics_separately() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ct X usize ct X usize
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -785,16 +785,16 @@ fn completes_const_and_type_generics_separately() {
} }
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo Foo st Foo Foo
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -809,8 +809,8 @@ fn completes_const_and_type_generics_separately() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ct X usize ct X usize
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -828,17 +828,17 @@ fn completes_const_and_type_generics_separately() {
fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {} fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {}
"#, "#,
expect![[r#" expect![[r#"
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo Foo st Foo Foo
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
tt Bar tt Bar
tt Trait tt Trait
un Union Union un Union Union
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
"#]], "#]],
@ -853,8 +853,8 @@ fn completes_const_and_type_generics_separately() {
fn foo<T: Bar<Baz<(), $0> = ()>>() {} fn foo<T: Bar<Baz<(), $0> = ()>>() {}
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ct X usize ct X usize
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -871,8 +871,8 @@ fn completes_const_and_type_generics_separately() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ct X usize ct X usize
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -890,8 +890,8 @@ fn completes_const_and_type_generics_separately() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ct X usize ct X usize
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -908,8 +908,8 @@ fn completes_const_and_type_generics_separately() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ct X usize ct X usize
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -924,8 +924,8 @@ fn completes_const_and_type_generics_separately() {
impl Foo<(), $0> for () {} impl Foo<(), $0> for () {}
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ct X usize ct X usize
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -942,8 +942,8 @@ fn completes_const_and_type_generics_separately() {
fn foo<T: Bar<X$0, ()>>() {} fn foo<T: Bar<X$0, ()>>() {}
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ct X usize ct X usize
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -957,7 +957,7 @@ struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
fn foo<'a>() { S::<F$0, _>; } fn foo<'a>() { S::<F$0, _>; }
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::
@ -970,7 +970,7 @@ struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
fn foo<'a>() { S::<'static, 'static, F$0, _>; } fn foo<'a>() { S::<'static, 'static, F$0, _>; }
"#, "#,
expect![[r#" expect![[r#"
ct CONST Unit ct CONST Unit
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
kw crate:: kw crate::
kw self:: kw self::

View file

@ -92,7 +92,7 @@ use self::{foo::*, bar$0};
"#, "#,
expect![[r#" expect![[r#"
md foo md foo
st S S st S S
"#]], "#]],
); );
} }
@ -179,7 +179,7 @@ struct Bar;
"#, "#,
expect![[r#" expect![[r#"
ma foo macro_rules! foo_ ma foo macro_rules! foo_
st Foo Foo st Foo Foo
"#]], "#]],
); );
} }
@ -203,8 +203,8 @@ impl Foo {
"#, "#,
expect![[r#" expect![[r#"
ev RecordVariant RecordVariant ev RecordVariant RecordVariant
ev TupleVariant TupleVariant ev TupleVariant TupleVariant
ev UnitVariant UnitVariant ev UnitVariant UnitVariant
"#]], "#]],
); );
} }
@ -257,7 +257,7 @@ mod a {
} }
"#, "#,
expect![[r#" expect![[r#"
ct A usize ct A usize
md b md b
kw super:: kw super::
"#]], "#]],
@ -450,9 +450,9 @@ pub fn foo() {}
marco_rules! m { () => {} } marco_rules! m { () => {} }
"#, "#,
expect![[r#" expect![[r#"
fn foo fn() fn foo fn()
md simd md simd
st S S st S S
"#]], "#]],
); );
} }

View file

@ -2,10 +2,10 @@
use hir::{ use hir::{
db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig, db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig,
ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, ItemInNs, ModPath, Module, ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics,
SemanticsScope, Trait, TyFingerprint, Type, SemanticsScope, Trait, TyFingerprint, Type,
}; };
use itertools::{EitherOrBoth, Itertools}; use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{ use syntax::{
ast::{self, make, HasName}, ast::{self, make, HasName},
@ -13,7 +13,6 @@ use syntax::{
}; };
use crate::{ use crate::{
helpers::item_name,
items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT}, items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT},
FxIndexSet, RootDatabase, FxIndexSet, RootDatabase,
}; };
@ -52,7 +51,7 @@ pub struct TraitImportCandidate {
#[derive(Debug)] #[derive(Debug)]
pub struct PathImportCandidate { pub struct PathImportCandidate {
/// Optional qualifier before name. /// Optional qualifier before name.
pub qualifier: Option<Vec<SmolStr>>, pub qualifier: Vec<SmolStr>,
/// The name the item (struct, trait, enum, etc.) should have. /// The name the item (struct, trait, enum, etc.) should have.
pub name: NameToImport, pub name: NameToImport,
} }
@ -264,7 +263,6 @@ impl ImportAssets {
Some(it) => it, Some(it) => it,
None => return <FxIndexSet<_>>::default().into_iter(), None => return <FxIndexSet<_>>::default().into_iter(),
}; };
let krate = self.module_with_candidate.krate(); let krate = self.module_with_candidate.krate();
let scope_definitions = self.scope_definitions(sema); let scope_definitions = self.scope_definitions(sema);
let mod_path = |item| { let mod_path = |item| {
@ -279,11 +277,14 @@ impl ImportAssets {
}; };
match &self.import_candidate { match &self.import_candidate {
ImportCandidate::Path(path_candidate) => { ImportCandidate::Path(path_candidate) => path_applicable_imports(
path_applicable_imports(sema, krate, path_candidate, mod_path, |item_to_import| { sema,
!scope_definitions.contains(&ScopeDef::from(item_to_import)) &scope,
}) krate,
} path_candidate,
mod_path,
|item_to_import| !scope_definitions.contains(&ScopeDef::from(item_to_import)),
),
ImportCandidate::TraitAssocItem(trait_candidate) ImportCandidate::TraitAssocItem(trait_candidate)
| ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items( | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
sema, sema,
@ -315,6 +316,7 @@ impl ImportAssets {
fn path_applicable_imports( fn path_applicable_imports(
sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
scope: &SemanticsScope<'_>,
current_crate: Crate, current_crate: Crate,
path_candidate: &PathImportCandidate, path_candidate: &PathImportCandidate,
mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
@ -322,8 +324,8 @@ fn path_applicable_imports(
) -> FxIndexSet<LocatedImport> { ) -> FxIndexSet<LocatedImport> {
let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered(); let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered();
match &path_candidate.qualifier { match &*path_candidate.qualifier {
None => { [] => {
items_locator::items_with_name( items_locator::items_with_name(
sema, sema,
current_crate, current_crate,
@ -348,89 +350,107 @@ fn path_applicable_imports(
.take(DEFAULT_QUERY_SEARCH_LIMIT.inner()) .take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
.collect() .collect()
} }
Some(qualifier) => items_locator::items_with_name( [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
sema, sema,
current_crate, current_crate,
path_candidate.name.clone(), NameToImport::Exact(first_qsegment.to_string(), true),
AssocSearchMode::Include, AssocSearchMode::Exclude,
) )
.filter_map(|item| import_for_item(sema.db, mod_path, qualifier, item, scope_filter)) .filter_map(|item| {
import_for_item(
sema,
scope,
mod_path,
&path_candidate.name,
item,
qualifier_rest,
scope_filter,
)
})
.take(DEFAULT_QUERY_SEARCH_LIMIT.inner()) .take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
.collect(), .collect(),
} }
} }
fn import_for_item( fn import_for_item(
db: &RootDatabase, sema: &Semantics<'_, RootDatabase>,
scope: &SemanticsScope<'_>,
mod_path: impl Fn(ItemInNs) -> Option<ModPath>, mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
candidate: &NameToImport,
resolved_qualifier: ItemInNs,
unresolved_qualifier: &[SmolStr], unresolved_qualifier: &[SmolStr],
original_item: ItemInNs,
scope_filter: impl Fn(ItemInNs) -> bool, scope_filter: impl Fn(ItemInNs) -> bool,
) -> Option<LocatedImport> { ) -> Option<LocatedImport> {
let _p = tracing::info_span!("ImportAssets::import_for_item").entered(); let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
let [first_segment, ..] = unresolved_qualifier else { return None };
let item_as_assoc = item_as_assoc(db, original_item); let qualifier = {
let mut adjusted_resolved_qualifier = resolved_qualifier;
let (original_item_candidate, trait_item_to_import) = match item_as_assoc { if !unresolved_qualifier.is_empty() {
Some(assoc_item) => match assoc_item.container(db) { match resolved_qualifier {
AssocItemContainer::Trait(trait_) => { ItemInNs::Types(ModuleDef::Module(module)) => {
let trait_ = ItemInNs::from(ModuleDef::from(trait_)); adjusted_resolved_qualifier = sema
(trait_, Some(trait_)) .resolve_mod_path_relative(module, unresolved_qualifier.iter().cloned())?
.next()?;
}
// can't resolve multiple segments for non-module item path bases
_ => return None,
} }
AssocItemContainer::Impl(impl_) => { }
(ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)), None)
} match adjusted_resolved_qualifier {
}, ItemInNs::Types(def) => def,
None => (original_item, None), _ => return None,
}
}; };
let import_path_candidate = mod_path(original_item_candidate)?; let import_path_candidate = mod_path(resolved_qualifier)?;
let ty = match qualifier {
let mut import_path_candidate_segments = import_path_candidate.segments().iter().rev(); ModuleDef::Module(module) => {
let predicate = |it: EitherOrBoth<&SmolStr, &Name>| match it { return items_locator::items_with_name_in_module(
// segments match, check next one sema,
EitherOrBoth::Both(a, b) if b.as_str() == &**a => None, module,
// segments mismatch / qualifier is longer than the path, bail out candidate.clone(),
EitherOrBoth::Both(..) | EitherOrBoth::Left(_) => Some(false), AssocSearchMode::Exclude,
// all segments match and we have exhausted the qualifier, proceed )
EitherOrBoth::Right(_) => Some(true), .find(|&it| scope_filter(it))
}; .map(|item| LocatedImport::new(import_path_candidate, resolved_qualifier, item))
if item_as_assoc.is_none() {
let item_name = item_name(db, original_item)?;
let last_segment = import_path_candidate_segments.next()?;
if *last_segment != item_name {
return None;
}
}
let ends_with = unresolved_qualifier
.iter()
.rev()
.zip_longest(import_path_candidate_segments)
.find_map(predicate)
.unwrap_or(true);
if !ends_with {
return None;
}
let segment_import = find_import_for_segment(db, original_item_candidate, first_segment)?;
Some(match (segment_import == original_item_candidate, trait_item_to_import) {
(true, Some(_)) => {
// FIXME we should be able to import both the trait and the segment,
// but it's unclear what to do with overlapping edits (merge imports?)
// especially in case of lazy completion edit resolutions.
return None;
}
(false, Some(trait_to_import)) if scope_filter(trait_to_import) => {
LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, original_item)
}
(true, None) if scope_filter(original_item_candidate) => {
LocatedImport::new(import_path_candidate, original_item_candidate, original_item)
}
(false, None) if scope_filter(segment_import) => {
LocatedImport::new(mod_path(segment_import)?, segment_import, original_item)
} }
// FIXME
ModuleDef::Trait(_) => return None,
// FIXME
ModuleDef::TraitAlias(_) => return None,
ModuleDef::TypeAlias(alias) => alias.ty(sema.db),
ModuleDef::BuiltinType(builtin) => builtin.ty(sema.db),
ModuleDef::Adt(adt) => adt.ty(sema.db),
_ => return None, _ => return None,
};
ty.iterate_path_candidates(sema.db, scope, &FxHashSet::default(), None, None, |assoc| {
// FIXME: Support extra trait imports
if assoc.container_or_implemented_trait(sema.db).is_some() {
return None;
}
let name = assoc.name(sema.db)?;
let is_match = match candidate {
NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
NameToImport::Prefix(text, false) => {
name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
name_char.eq_ignore_ascii_case(&candidate_char)
})
}
NameToImport::Exact(text, true) => name.as_str() == text,
NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
NameToImport::Fuzzy(text, false) => text
.chars()
.all(|c| name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))),
};
if !is_match {
return None;
}
Some(LocatedImport::new(
import_path_candidate.clone(),
resolved_qualifier,
assoc_to_item(assoc),
))
}) })
} }
@ -453,45 +473,6 @@ fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Optio
}) })
} }
fn find_import_for_segment(
db: &RootDatabase,
original_item: ItemInNs,
unresolved_first_segment: &str,
) -> Option<ItemInNs> {
let segment_is_name = item_name(db, original_item)
.map(|name| name.eq_ident(unresolved_first_segment))
.unwrap_or(false);
Some(if segment_is_name {
original_item
} else {
let matching_module =
module_with_segment_name(db, unresolved_first_segment, original_item)?;
ItemInNs::from(ModuleDef::from(matching_module))
})
}
fn module_with_segment_name(
db: &RootDatabase,
segment_name: &str,
candidate: ItemInNs,
) -> Option<Module> {
let mut current_module = match candidate {
ItemInNs::Types(module_def_id) => module_def_id.module(db),
ItemInNs::Values(module_def_id) => module_def_id.module(db),
ItemInNs::Macros(macro_def_id) => ModuleDef::from(macro_def_id).module(db),
};
while let Some(module) = current_module {
if let Some(module_name) = module.name(db) {
if module_name.eq_ident(segment_name) {
return Some(module);
}
}
current_module = module.parent(db);
}
None
}
fn trait_applicable_items( fn trait_applicable_items(
sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
current_crate: Crate, current_crate: Crate,
@ -703,7 +684,7 @@ impl ImportCandidate {
return None; return None;
} }
Some(ImportCandidate::Path(PathImportCandidate { Some(ImportCandidate::Path(PathImportCandidate {
qualifier: None, qualifier: vec![],
name: NameToImport::exact_case_sensitive(name.to_string()), name: NameToImport::exact_case_sensitive(name.to_string()),
})) }))
} }
@ -730,7 +711,7 @@ fn path_import_candidate(
.segments() .segments()
.map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text()))) .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text())))
.collect::<Option<Vec<_>>>()?; .collect::<Option<Vec<_>>>()?;
ImportCandidate::Path(PathImportCandidate { qualifier: Some(qualifier), name }) ImportCandidate::Path(PathImportCandidate { qualifier, name })
} else { } else {
return None; return None;
} }
@ -754,10 +735,10 @@ fn path_import_candidate(
} }
Some(_) => return None, Some(_) => return None,
}, },
None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }), None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name }),
}) })
} }
fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> { fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)) item.into_module_def().as_assoc_item(db)
} }

View file

@ -3,10 +3,14 @@
//! The main reason for this module to exist is the fact that project's items and dependencies' items //! The main reason for this module to exist is the fact that project's items and dependencies' items
//! are located in different caches, with different APIs. //! are located in different caches, with different APIs.
use either::Either; use either::Either;
use hir::{import_map, Crate, ItemInNs, Semantics}; use hir::{import_map, Crate, ItemInNs, Module, Semantics};
use limit::Limit; use limit::Limit;
use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase}; use crate::{
imports::import_assets::NameToImport,
symbol_index::{self, SymbolsDatabase as _},
RootDatabase,
};
/// A value to use, when uncertain which limit to pick. /// A value to use, when uncertain which limit to pick.
pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100); pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100);
@ -20,8 +24,7 @@ pub fn items_with_name<'a>(
name: NameToImport, name: NameToImport,
assoc_item_search: AssocSearchMode, assoc_item_search: AssocSearchMode,
) -> impl Iterator<Item = ItemInNs> + 'a { ) -> impl Iterator<Item = ItemInNs> + 'a {
let krate_name = krate.display_name(sema.db).map(|name| name.to_string()); let _p = tracing::info_span!("items_with_name", name = name.text(), assoc_item_search = ?assoc_item_search, crate = ?krate.display_name(sema.db).map(|name| name.to_string()))
let _p = tracing::info_span!("items_with_name", name = name.text(), assoc_item_search = ?assoc_item_search, crate = ?krate_name)
.entered(); .entered();
let prefix = matches!(name, NameToImport::Prefix(..)); let prefix = matches!(name, NameToImport::Prefix(..));
@ -66,6 +69,54 @@ pub fn items_with_name<'a>(
find_items(sema, krate, local_query, external_query) find_items(sema, krate, local_query, external_query)
} }
/// Searches for importable items with the given name in the crate and its dependencies.
pub fn items_with_name_in_module<'a>(
sema: &'a Semantics<'_, RootDatabase>,
module: Module,
name: NameToImport,
assoc_item_search: AssocSearchMode,
) -> impl Iterator<Item = ItemInNs> + 'a {
let _p = tracing::info_span!("items_with_name_in", name = name.text(), assoc_item_search = ?assoc_item_search, ?module)
.entered();
let prefix = matches!(name, NameToImport::Prefix(..));
let local_query = match name {
NameToImport::Prefix(exact_name, case_sensitive)
| NameToImport::Exact(exact_name, case_sensitive) => {
let mut local_query = symbol_index::Query::new(exact_name.clone());
local_query.assoc_search_mode(assoc_item_search);
if prefix {
local_query.prefix();
} else {
local_query.exact();
}
if case_sensitive {
local_query.case_sensitive();
}
local_query
}
NameToImport::Fuzzy(fuzzy_search_string, case_sensitive) => {
let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
local_query.fuzzy();
local_query.assoc_search_mode(assoc_item_search);
if case_sensitive {
local_query.case_sensitive();
}
local_query
}
};
let mut local_results = Vec::new();
local_query.search(&[sema.db.module_symbols(module)], |local_candidate| {
local_results.push(match local_candidate.def {
hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
def => ItemInNs::from(def),
})
});
local_results.into_iter()
}
fn find_items<'a>( fn find_items<'a>(
sema: &'a Semantics<'_, RootDatabase>, sema: &'a Semantics<'_, RootDatabase>,
krate: Crate, krate: Crate,

View file

@ -377,6 +377,8 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S
return None; return None;
} }
name name
} else if let Some(inner_ty) = ty.remove_ref() {
return name_of_type(&inner_ty, db, edition);
} else { } else {
return None; return None;
}; };

View file

@ -778,4 +778,20 @@ fn bar(mut v: Union2) {
"#, "#,
) )
} }
#[test]
fn raw_ref_reborrow_is_safe() {
check_diagnostics(
r#"
fn main() {
let ptr: *mut i32;
let _addr = &raw const *ptr;
let local = 1;
let ptr = &local as *const i32;
let _addr = &raw const *ptr;
}
"#,
)
}
} }

View file

@ -90,7 +90,9 @@ fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<A
make::ty("()") make::ty("()")
}; };
if !is_editable_crate(target_module.krate(), ctx.sema.db) { if !is_editable_crate(target_module.krate(), ctx.sema.db)
|| SyntaxKind::from_keyword(field_name, ctx.edition).is_some()
{
return None; return None;
} }
@ -501,4 +503,19 @@ fn main() {}
"#, "#,
) )
} }
#[test]
fn regression_18683() {
check_diagnostics(
r#"
struct S;
impl S {
fn f(self) {
self.self
// ^^^^ error: no field `self` on type `S`
}
}
"#,
);
}
} }

View file

@ -7,20 +7,19 @@ pub(crate) fn unresolved_ident(
ctx: &DiagnosticsContext<'_>, ctx: &DiagnosticsContext<'_>,
d: &hir::UnresolvedIdent, d: &hir::UnresolvedIdent,
) -> Diagnostic { ) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr( let mut range =
ctx, ctx.sema.diagnostics_display_range(d.node.map(|(node, _)| node.syntax_node_ptr()));
DiagnosticCode::RustcHardError("E0425"), if let Some(in_node_range) = d.node.value.1 {
"no such value in this scope", range.range = in_node_range + range.range.start();
d.expr_or_pat.map(Into::into), }
) Diagnostic::new(DiagnosticCode::RustcHardError("E0425"), "no such value in this scope", range)
.experimental() .experimental()
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::tests::check_diagnostics; use crate::tests::check_diagnostics;
// FIXME: This should show a diagnostic
#[test] #[test]
fn feature() { fn feature() {
check_diagnostics( check_diagnostics(
@ -28,6 +27,7 @@ mod tests {
//- minicore: fmt //- minicore: fmt
fn main() { fn main() {
format_args!("{unresolved}"); format_args!("{unresolved}");
// ^^^^^^^^^^ error: no such value in this scope
} }
"#, "#,
) )

View file

@ -16,7 +16,7 @@ use ide_db::{
}; };
use itertools::Itertools; use itertools::Itertools;
use span::{Edition, TextSize}; use span::{Edition, TextSize};
use stdx::{always, format_to}; use stdx::format_to;
use syntax::{ use syntax::{
ast::{self, AstNode}, ast::{self, AstNode},
SmolStr, SyntaxNode, ToSmolStr, SmolStr, SyntaxNode, ToSmolStr,
@ -130,14 +130,7 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
// In case an expansion creates multiple runnables we want to name them to avoid emitting a bunch of equally named runnables. // In case an expansion creates multiple runnables we want to name them to avoid emitting a bunch of equally named runnables.
let mut in_macro_expansion = FxHashMap::<hir::HirFileId, Vec<Runnable>>::default(); let mut in_macro_expansion = FxHashMap::<hir::HirFileId, Vec<Runnable>>::default();
let mut add_opt = |runnable: Option<Runnable>, def| { let mut add_opt = |runnable: Option<Runnable>, def| {
if let Some(runnable) = runnable.filter(|runnable| { if let Some(runnable) = runnable.filter(|runnable| runnable.nav.file_id == file_id) {
always!(
runnable.nav.file_id == file_id,
"tried adding a runnable pointing to a different file: {:?} for {:?}",
runnable.kind,
file_id
)
}) {
if let Some(def) = def { if let Some(def) = def {
let file_id = match def { let file_id = match def {
Definition::Module(it) => { Definition::Module(it) => {
@ -161,13 +154,7 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
Definition::SelfType(impl_) => runnable_impl(&sema, &impl_), Definition::SelfType(impl_) => runnable_impl(&sema, &impl_),
_ => None, _ => None,
}; };
add_opt( add_opt(runnable.or_else(|| module_def_doctest(sema.db, def)), Some(def));
runnable
.or_else(|| module_def_doctest(sema.db, def))
// #[macro_export] mbe macros are declared in the root, while their definition may reside in a different module
.filter(|it| it.nav.file_id == file_id),
Some(def),
);
if let Definition::SelfType(impl_) = def { if let Definition::SelfType(impl_) = def {
impl_.items(db).into_iter().for_each(|assoc| { impl_.items(db).into_iter().for_each(|assoc| {
let runnable = match assoc { let runnable = match assoc {

View file

@ -174,6 +174,9 @@ fn on_delimited_node_typed(
kinds: &[fn(SyntaxKind) -> bool], kinds: &[fn(SyntaxKind) -> bool],
) -> Option<TextEdit> { ) -> Option<TextEdit> {
let t = reparsed.syntax().token_at_offset(offset).right_biased()?; let t = reparsed.syntax().token_at_offset(offset).right_biased()?;
if t.prev_token().map_or(false, |t| t.kind().is_any_identifier()) {
return None;
}
let (filter, node) = t let (filter, node) = t
.parent_ancestors() .parent_ancestors()
.take_while(|n| n.text_range().start() == offset) .take_while(|n| n.text_range().start() == offset)
@ -1091,6 +1094,22 @@ fn f() {
); );
} }
#[test]
fn preceding_whitespace_is_significant_for_closing_brackets() {
type_char_noop(
'(',
r#"
fn f() { a.b$0if true {} }
"#,
);
type_char_noop(
'(',
r#"
fn f() { foo$0{} }
"#,
);
}
#[test] #[test]
fn adds_closing_parenthesis_for_pat() { fn adds_closing_parenthesis_for_pat() {
type_char( type_char(

View file

@ -487,7 +487,7 @@ impl ProcMacroExpander for Expander {
match self.0.expand( match self.0.expand(
subtree, subtree,
attrs, attrs,
env.clone(), env.clone().into(),
def_site, def_site,
call_site, call_site,
mixed_site, mixed_site,

View file

@ -72,8 +72,19 @@ pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool, is_in_ext
// macro_rules! () // macro_rules! ()
// macro_rules! [] // macro_rules! []
if paths::is_use_path_start(p) { if paths::is_use_path_start(p) {
macro_call(p, m); paths::use_path(p);
return; // Do not create a MACRO_CALL node here if this isn't a macro call, this causes problems with completion.
// test_err path_item_without_excl
// foo
if p.at(T![!]) {
macro_call(p, m);
return;
} else {
m.complete(p, ERROR);
p.error("expected an item");
return;
}
} }
m.abandon(p); m.abandon(p);
@ -410,8 +421,7 @@ fn fn_(p: &mut Parser<'_>, m: Marker) {
} }
fn macro_call(p: &mut Parser<'_>, m: Marker) { fn macro_call(p: &mut Parser<'_>, m: Marker) {
assert!(paths::is_use_path_start(p)); assert!(p.at(T![!]));
paths::use_path(p);
match macro_call_after_excl(p) { match macro_call_after_excl(p) {
BlockLike::Block => (), BlockLike::Block => (),
BlockLike::NotBlock => { BlockLike::NotBlock => {

View file

@ -30,22 +30,20 @@ fn source_file() {
TopEntryPoint::SourceFile, TopEntryPoint::SourceFile,
"@error@", "@error@",
expect![[r#" expect![[r#"
SOURCE_FILE SOURCE_FILE
ERROR ERROR
AT "@" AT "@"
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
IDENT "error" IDENT "error"
ERROR ERROR
AT "@" AT "@"
error 0: expected an item error 0: expected an item
error 6: expected BANG error 6: expected an item
error 6: expected `{`, `[`, `(` error 6: expected an item
error 6: expected SEMICOLON "#]],
error 6: expected an item
"#]],
); );
} }

View file

@ -775,6 +775,10 @@ mod err {
run_and_expect_errors("test_data/parser/inline/err/missing_fn_param_type.rs"); run_and_expect_errors("test_data/parser/inline/err/missing_fn_param_type.rs");
} }
#[test] #[test]
fn path_item_without_excl() {
run_and_expect_errors("test_data/parser/inline/err/path_item_without_excl.rs");
}
#[test]
fn pointer_type_no_mutability() { fn pointer_type_no_mutability() {
run_and_expect_errors("test_data/parser/inline/err/pointer_type_no_mutability.rs"); run_and_expect_errors("test_data/parser/inline/err/pointer_type_no_mutability.rs");
} }

View file

@ -10,20 +10,20 @@ SOURCE_FILE
USE_KW "use" USE_KW "use"
ERROR ERROR
SLASH "/" SLASH "/"
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
IDENT "bin" IDENT "bin"
ERROR ERROR
SLASH "/" SLASH "/"
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
IDENT "env" IDENT "env"
WHITESPACE " " WHITESPACE " "
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
@ -33,13 +33,7 @@ error 23: expected `[`
error 23: expected an item error 23: expected an item
error 27: expected one of `*`, `::`, `{`, `self`, `super` or an identifier error 27: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
error 28: expected SEMICOLON error 28: expected SEMICOLON
error 31: expected BANG
error 31: expected `{`, `[`, `(`
error 31: expected SEMICOLON
error 31: expected an item error 31: expected an item
error 35: expected BANG error 31: expected an item
error 35: expected `{`, `[`, `(` error 35: expected an item
error 35: expected SEMICOLON error 41: expected an item
error 41: expected BANG
error 41: expected `{`, `[`, `(`
error 41: expected SEMICOLON

View file

@ -14,14 +14,15 @@ SOURCE_FILE
WHITESPACE "\n" WHITESPACE "\n"
R_CURLY "}" R_CURLY "}"
WHITESPACE "\n\n" WHITESPACE "\n\n"
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
IDENT "bar" IDENT "bar"
TOKEN_TREE ERROR
L_PAREN "(" L_PAREN "("
R_PAREN ")" ERROR
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
ERROR ERROR
L_CURLY "{" L_CURLY "{"
@ -75,6 +76,7 @@ SOURCE_FILE
WHITESPACE "\n" WHITESPACE "\n"
R_CURLY "}" R_CURLY "}"
WHITESPACE "\n" WHITESPACE "\n"
error 17: expected BANG error 17: expected an item
error 19: expected SEMICOLON error 17: expected an item
error 18: expected an item
error 20: expected an item error 20: expected an item

View file

@ -46,7 +46,7 @@ SOURCE_FILE
ERROR ERROR
AT "@" AT "@"
WHITESPACE " " WHITESPACE " "
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
@ -72,9 +72,7 @@ error 67: expected R_ANGLE
error 67: expected R_PAREN error 67: expected R_PAREN
error 67: expected SEMICOLON error 67: expected SEMICOLON
error 67: expected an item error 67: expected an item
error 72: expected BANG error 72: expected an item
error 72: expected `{`, `[`, `(`
error 72: expected SEMICOLON
error 72: expected an item error 72: expected an item
error 73: expected an item error 73: expected an item
error 79: expected an item error 79: expected an item

View file

@ -26,14 +26,15 @@ SOURCE_FILE
ERROR ERROR
FN_KW "fn" FN_KW "fn"
WHITESPACE " " WHITESPACE " "
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
IDENT "bar" IDENT "bar"
TOKEN_TREE ERROR
L_PAREN "(" L_PAREN "("
R_PAREN ")" ERROR
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
ERROR ERROR
L_CURLY "{" L_CURLY "{"
@ -43,6 +44,7 @@ error 6: expected fn, trait or impl
error 38: expected a name error 38: expected a name
error 40: missing type for `const` or `static` error 40: missing type for `const` or `static`
error 40: expected SEMICOLON error 40: expected SEMICOLON
error 44: expected BANG error 44: expected an item
error 46: expected SEMICOLON error 44: expected an item
error 45: expected an item
error 47: expected an item error 47: expected an item

View file

@ -12,15 +12,16 @@ SOURCE_FILE
ERROR ERROR
USE_KW "use" USE_KW "use"
WHITESPACE " " WHITESPACE " "
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
IDENT "std" IDENT "std"
ERROR
SEMICOLON ";" SEMICOLON ";"
WHITESPACE "\n" WHITESPACE "\n"
error 8: expected R_ANGLE error 8: expected R_ANGLE
error 8: expected type error 8: expected type
error 11: expected `{` error 11: expected `{`
error 15: expected BANG error 15: expected an item
error 15: expected `{`, `[`, `(` error 15: expected an item

View file

@ -1,5 +1,5 @@
SOURCE_FILE SOURCE_FILE
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
@ -22,7 +22,7 @@ SOURCE_FILE
ERROR ERROR
ASYNC_KW "async" ASYNC_KW "async"
WHITESPACE " " WHITESPACE " "
MACRO_CALL ERROR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
@ -42,10 +42,6 @@ SOURCE_FILE
L_CURLY "{" L_CURLY "{"
R_CURLY "}" R_CURLY "}"
WHITESPACE "\n" WHITESPACE "\n"
error 3: expected BANG error 3: expected an item
error 3: expected `{`, `[`, `(`
error 3: expected SEMICOLON
error 24: expected fn, trait or impl error 24: expected fn, trait or impl
error 28: expected BANG error 28: expected an item
error 28: expected `{`, `[`, `(`
error 28: expected SEMICOLON

View file

@ -0,0 +1,8 @@
SOURCE_FILE
ERROR
PATH
PATH_SEGMENT
NAME_REF
IDENT "foo"
WHITESPACE "\n"
error 3: expected an item

View file

@ -14,10 +14,9 @@ doctest = false
[dependencies] [dependencies]
camino.workspace = true camino.workspace = true
serde = { workspace = true, optional = true }
[features] [features]
serde1 = ["camino/serde1", "dep:serde"] serde1 = ["camino/serde1"]
[lints] [lints]
workspace = true workspace = true

View file

@ -14,6 +14,7 @@ doctest = false
[dependencies] [dependencies]
serde.workspace = true serde.workspace = true
serde_derive.workspace = true
serde_json = { workspace = true, features = ["unbounded_depth"] } serde_json = { workspace = true, features = ["unbounded_depth"] }
tracing.workspace = true tracing.workspace = true
rustc-hash.workspace = true rustc-hash.workspace = true
@ -23,11 +24,9 @@ indexmap.workspace = true
paths = { workspace = true, features = ["serde1"] } paths = { workspace = true, features = ["serde1"] }
tt.workspace = true tt.workspace = true
stdx.workspace = true stdx.workspace = true
# Ideally this crate would not depend on salsa things, but we need span information here which wraps # span = {workspace = true, default-features = false} does not work
# InternIds for the syntax context span = { path = "../span", version = "0.0.0", default-features = false}
span.workspace = true
# only here due to the `Env` newtype :/
base-db.workspace = true
intern.workspace = true intern.workspace = true
[lints] [lints]

View file

@ -9,7 +9,6 @@ pub mod json;
pub mod msg; pub mod msg;
mod process; mod process;
use base_db::Env;
use paths::{AbsPath, AbsPathBuf}; use paths::{AbsPath, AbsPathBuf};
use span::Span; use span::Span;
use std::{fmt, io, sync::Arc}; use std::{fmt, io, sync::Arc};
@ -148,7 +147,7 @@ impl ProcMacro {
&self, &self,
subtree: &tt::Subtree<Span>, subtree: &tt::Subtree<Span>,
attr: Option<&tt::Subtree<Span>>, attr: Option<&tt::Subtree<Span>>,
env: Env, env: Vec<(String, String)>,
def_site: Span, def_site: Span,
call_site: Span, call_site: Span,
mixed_site: Span, mixed_site: Span,
@ -179,7 +178,7 @@ impl ProcMacro {
}, },
}, },
lib: self.dylib_path.to_path_buf().into(), lib: self.dylib_path.to_path_buf().into(),
env: env.into(), env,
current_dir, current_dir,
}; };

View file

@ -4,7 +4,8 @@ pub(crate) mod flat;
use std::io::{self, BufRead, Write}; use std::io::{self, BufRead, Write};
use paths::Utf8PathBuf; use paths::Utf8PathBuf;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::de::DeserializeOwned;
use serde_derive::{Deserialize, Serialize};
use crate::ProcMacroKind; use crate::ProcMacroKind;
@ -123,7 +124,7 @@ impl ExpnGlobals {
} }
} }
pub trait Message: Serialize + DeserializeOwned { pub trait Message: serde::Serialize + DeserializeOwned {
fn read<R: BufRead>( fn read<R: BufRead>(
from_proto: ProtocolRead<R>, from_proto: ProtocolRead<R>,
inp: &mut R, inp: &mut R,

View file

@ -39,10 +39,10 @@ use std::collections::VecDeque;
use intern::Symbol; use intern::Symbol;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange}; use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange};
use crate::msg::EXTENDED_LEAF_DATA; use crate::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA};
pub type SpanDataIndexMap = pub type SpanDataIndexMap =
indexmap::IndexSet<Span, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>; indexmap::IndexSet<Span, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
@ -145,7 +145,11 @@ impl FlatTree {
w.write(subtree); w.write(subtree);
FlatTree { FlatTree {
subtree: write_vec(w.subtree, SubtreeRepr::write), subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
write_vec(w.subtree, SubtreeRepr::write_with_close_span)
} else {
write_vec(w.subtree, SubtreeRepr::write)
},
literal: if version >= EXTENDED_LEAF_DATA { literal: if version >= EXTENDED_LEAF_DATA {
write_vec(w.literal, LiteralRepr::write_with_kind) write_vec(w.literal, LiteralRepr::write_with_kind)
} else { } else {
@ -179,7 +183,11 @@ impl FlatTree {
w.write(subtree); w.write(subtree);
FlatTree { FlatTree {
subtree: write_vec(w.subtree, SubtreeRepr::write), subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
write_vec(w.subtree, SubtreeRepr::write_with_close_span)
} else {
write_vec(w.subtree, SubtreeRepr::write)
},
literal: if version >= EXTENDED_LEAF_DATA { literal: if version >= EXTENDED_LEAF_DATA {
write_vec(w.literal, LiteralRepr::write_with_kind) write_vec(w.literal, LiteralRepr::write_with_kind)
} else { } else {
@ -202,7 +210,11 @@ impl FlatTree {
span_data_table: &SpanDataIndexMap, span_data_table: &SpanDataIndexMap,
) -> tt::Subtree<Span> { ) -> tt::Subtree<Span> {
Reader { Reader {
subtree: read_vec(self.subtree, SubtreeRepr::read), subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
read_vec(self.subtree, SubtreeRepr::read_with_close_span)
} else {
read_vec(self.subtree, SubtreeRepr::read)
},
literal: if version >= EXTENDED_LEAF_DATA { literal: if version >= EXTENDED_LEAF_DATA {
read_vec(self.literal, LiteralRepr::read_with_kind) read_vec(self.literal, LiteralRepr::read_with_kind)
} else { } else {
@ -224,7 +236,11 @@ impl FlatTree {
pub fn to_subtree_unresolved(self, version: u32) -> tt::Subtree<TokenId> { pub fn to_subtree_unresolved(self, version: u32) -> tt::Subtree<TokenId> {
Reader { Reader {
subtree: read_vec(self.subtree, SubtreeRepr::read), subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
read_vec(self.subtree, SubtreeRepr::read_with_close_span)
} else {
read_vec(self.subtree, SubtreeRepr::read)
},
literal: if version >= EXTENDED_LEAF_DATA { literal: if version >= EXTENDED_LEAF_DATA {
read_vec(self.literal, LiteralRepr::read_with_kind) read_vec(self.literal, LiteralRepr::read_with_kind)
} else { } else {
@ -257,7 +273,26 @@ fn write_vec<T, F: Fn(T) -> [u32; N], const N: usize>(xs: Vec<T>, f: F) -> Vec<u
} }
impl SubtreeRepr { impl SubtreeRepr {
fn write(self) -> [u32; 5] { fn write(self) -> [u32; 4] {
let kind = match self.kind {
tt::DelimiterKind::Invisible => 0,
tt::DelimiterKind::Parenthesis => 1,
tt::DelimiterKind::Brace => 2,
tt::DelimiterKind::Bracket => 3,
};
[self.open.0, kind, self.tt[0], self.tt[1]]
}
fn read([open, kind, lo, len]: [u32; 4]) -> SubtreeRepr {
let kind = match kind {
0 => tt::DelimiterKind::Invisible,
1 => tt::DelimiterKind::Parenthesis,
2 => tt::DelimiterKind::Brace,
3 => tt::DelimiterKind::Bracket,
other => panic!("bad kind {other}"),
};
SubtreeRepr { open: TokenId(open), close: TokenId(!0), kind, tt: [lo, len] }
}
fn write_with_close_span(self) -> [u32; 5] {
let kind = match self.kind { let kind = match self.kind {
tt::DelimiterKind::Invisible => 0, tt::DelimiterKind::Invisible => 0,
tt::DelimiterKind::Parenthesis => 1, tt::DelimiterKind::Parenthesis => 1,
@ -266,7 +301,7 @@ impl SubtreeRepr {
}; };
[self.open.0, self.close.0, kind, self.tt[0], self.tt[1]] [self.open.0, self.close.0, kind, self.tt[0], self.tt[1]]
} }
fn read([open, close, kind, lo, len]: [u32; 5]) -> SubtreeRepr { fn read_with_close_span([open, close, kind, lo, len]: [u32; 5]) -> SubtreeRepr {
let kind = match kind { let kind = match kind {
0 => tt::DelimiterKind::Invisible, 0 => tt::DelimiterKind::Invisible,
1 => tt::DelimiterKind::Parenthesis, 1 => tt::DelimiterKind::Parenthesis,

View file

@ -56,25 +56,8 @@ impl ProcMacroProcessSrv {
match srv.version_check() { match srv.version_check() {
Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::new( Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
format!( format!( "The version of the proc-macro server ({v}) in your Rust toolchain is newer than the version supported by your rust-analyzer ({CURRENT_API_VERSION}).
"The version of the proc-macro server ({v}) in your Rust toolchain \ This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain."
is newer than the version supported by your rust-analyzer ({CURRENT_API_VERSION}).
\
This will prevent proc-macro expansion from working. \
Please consider updating your rust-analyzer to ensure compatibility with your \
current toolchain."
),
)),
Ok(v) if v < RUST_ANALYZER_SPAN_SUPPORT => Err(io::Error::new(
io::ErrorKind::Other,
format!(
"The version of the proc-macro server ({v}) in your Rust toolchain \
is too old and no longer supported by your rust-analyzer which requires\
version {RUST_ANALYZER_SPAN_SUPPORT} or higher.
\
This will prevent proc-macro expansion from working. \
Please consider updating your toolchain or downgrading your rust-analyzer \
to ensure compatibility with your current toolchain."
), ),
)), )),
Ok(v) => { Ok(v) => {
@ -89,10 +72,10 @@ impl ProcMacroProcessSrv {
tracing::info!("Proc-macro server span mode: {:?}", srv.mode); tracing::info!("Proc-macro server span mode: {:?}", srv.mode);
Ok(srv) Ok(srv)
} }
Err(e) => Err(io::Error::new( Err(e) => {
io::ErrorKind::Other, tracing::info!(%e, "proc-macro version check failed, restarting and assuming version 0");
format!("Failed to fetch proc-macro server version: {e}"), create_srv(false)
)), }
} }
} }

View file

@ -21,8 +21,8 @@ stdx.workspace = true
tt.workspace = true tt.workspace = true
syntax-bridge.workspace = true syntax-bridge.workspace = true
paths.workspace = true paths.workspace = true
base-db.workspace = true # span = {workspace = true, default-features = false} does not work
span.workspace = true span = { path = "../span", version = "0.0.0", default-features = false}
proc-macro-api.workspace = true proc-macro-api.workspace = true
intern.workspace = true intern.workspace = true

View file

@ -19,6 +19,7 @@ rustc-hash.workspace = true
semver.workspace = true semver.workspace = true
serde_json.workspace = true serde_json.workspace = true
serde.workspace = true serde.workspace = true
serde_derive.workspace = true
tracing.workspace = true tracing.workspace = true
triomphe.workspace = true triomphe.workspace = true
la-arena.workspace = true la-arena.workspace = true

View file

@ -15,7 +15,7 @@ use itertools::Itertools;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize; use serde::Deserialize as _;
use toolchain::Tool; use toolchain::Tool;
use crate::{ use crate::{

View file

@ -8,7 +8,7 @@ use cargo_metadata::{CargoOpt, MetadataCommand};
use la_arena::{Arena, Idx}; use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize; use serde_derive::Deserialize;
use serde_json::from_value; use serde_json::from_value;
use span::Edition; use span::Edition;
use toolchain::Tool; use toolchain::Tool;

View file

@ -20,7 +20,7 @@ parking_lot = "0.12.1"
rustc-hash = "2.0.0" rustc-hash = "2.0.0"
smallvec = "1.0.0" smallvec = "1.0.0"
oorandom = "11" oorandom = "11"
triomphe = "0.1.11" triomphe.workspace = true
itertools.workspace = true itertools.workspace = true
ra-salsa-macros = { version = "0.0.0", path = "ra-salsa-macros", package = "salsa-macros" } ra-salsa-macros = { version = "0.0.0", path = "ra-salsa-macros", package = "salsa-macros" }

View file

@ -36,6 +36,7 @@ rayon.workspace = true
rustc-hash.workspace = true rustc-hash.workspace = true
serde_json = { workspace = true, features = ["preserve_order"] } serde_json = { workspace = true, features = ["preserve_order"] }
serde.workspace = true serde.workspace = true
serde_derive.workspace = true
tenthash = "0.4.0" tenthash = "0.4.0"
num_cpus = "1.15.0" num_cpus = "1.15.0"
mimalloc = { version = "0.1.30", default-features = false, optional = true } mimalloc = { version = "0.1.30", default-features = false, optional = true }

View file

@ -644,7 +644,8 @@ config_data! {
/// Aliased as `"checkOnSave.targets"`. /// Aliased as `"checkOnSave.targets"`.
check_targets | checkOnSave_targets | checkOnSave_target: Option<CheckOnSaveTargets> = None, check_targets | checkOnSave_targets | checkOnSave_target: Option<CheckOnSaveTargets> = None,
/// Whether `--workspace` should be passed to `cargo check`. /// Whether `--workspace` should be passed to `cargo check`.
/// If false, `-p <package>` will be passed instead. /// If false, `-p <package>` will be passed instead if applicable. In case it is not, no
/// check will be performed.
check_workspace: bool = true, check_workspace: bool = true,
/// These proc-macros will be ignored when trying to expand them. /// These proc-macros will be ignored when trying to expand them.

View file

@ -3,6 +3,7 @@ pub(crate) mod to_proto;
use std::mem; use std::mem;
use cargo_metadata::PackageId;
use ide::FileId; use ide::FileId;
use ide_db::FxHashMap; use ide_db::FxHashMap;
use itertools::Itertools; use itertools::Itertools;
@ -13,7 +14,8 @@ use triomphe::Arc;
use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext, main_loop::DiagnosticsTaskKind}; use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext, main_loop::DiagnosticsTaskKind};
pub(crate) type CheckFixes = Arc<IntMap<usize, IntMap<FileId, Vec<Fix>>>>; pub(crate) type CheckFixes =
Arc<IntMap<usize, FxHashMap<Option<Arc<PackageId>>, IntMap<FileId, Vec<Fix>>>>>;
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct DiagnosticsMapConfig { pub struct DiagnosticsMapConfig {
@ -31,7 +33,10 @@ pub(crate) struct DiagnosticCollection {
pub(crate) native_syntax: IntMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>, pub(crate) native_syntax: IntMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>,
pub(crate) native_semantic: IntMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>, pub(crate) native_semantic: IntMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>,
// FIXME: should be Vec<flycheck::Diagnostic> // FIXME: should be Vec<flycheck::Diagnostic>
pub(crate) check: IntMap<usize, IntMap<FileId, Vec<lsp_types::Diagnostic>>>, pub(crate) check: IntMap<
usize,
FxHashMap<Option<Arc<PackageId>>, IntMap<FileId, Vec<lsp_types::Diagnostic>>>,
>,
pub(crate) check_fixes: CheckFixes, pub(crate) check_fixes: CheckFixes,
changes: IntSet<FileId>, changes: IntSet<FileId>,
/// Counter for supplying a new generation number for diagnostics. /// Counter for supplying a new generation number for diagnostics.
@ -50,18 +55,37 @@ pub(crate) struct Fix {
impl DiagnosticCollection { impl DiagnosticCollection {
pub(crate) fn clear_check(&mut self, flycheck_id: usize) { pub(crate) fn clear_check(&mut self, flycheck_id: usize) {
if let Some(it) = Arc::make_mut(&mut self.check_fixes).get_mut(&flycheck_id) { let Some(check) = self.check.get_mut(&flycheck_id) else {
it.clear(); return;
} };
if let Some(it) = self.check.get_mut(&flycheck_id) { self.changes.extend(check.drain().flat_map(|(_, v)| v.into_keys()));
self.changes.extend(it.drain().map(|(key, _value)| key)); if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(&flycheck_id) {
fixes.clear();
} }
} }
pub(crate) fn clear_check_all(&mut self) { pub(crate) fn clear_check_all(&mut self) {
Arc::make_mut(&mut self.check_fixes).clear(); Arc::make_mut(&mut self.check_fixes).clear();
self.changes self.changes.extend(
.extend(self.check.values_mut().flat_map(|it| it.drain().map(|(key, _value)| key))) self.check.values_mut().flat_map(|it| it.drain().flat_map(|(_, v)| v.into_keys())),
)
}
pub(crate) fn clear_check_for_package(
&mut self,
flycheck_id: usize,
package_id: Arc<PackageId>,
) {
let Some(check) = self.check.get_mut(&flycheck_id) else {
return;
};
let package_id = Some(package_id);
if let Some(checks) = check.remove(&package_id) {
self.changes.extend(checks.into_keys());
}
if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(&flycheck_id) {
fixes.remove(&package_id);
}
} }
pub(crate) fn clear_native_for(&mut self, file_id: FileId) { pub(crate) fn clear_native_for(&mut self, file_id: FileId) {
@ -73,11 +97,19 @@ impl DiagnosticCollection {
pub(crate) fn add_check_diagnostic( pub(crate) fn add_check_diagnostic(
&mut self, &mut self,
flycheck_id: usize, flycheck_id: usize,
package_id: &Option<Arc<PackageId>>,
file_id: FileId, file_id: FileId,
diagnostic: lsp_types::Diagnostic, diagnostic: lsp_types::Diagnostic,
fix: Option<Box<Fix>>, fix: Option<Box<Fix>>,
) { ) {
let diagnostics = self.check.entry(flycheck_id).or_default().entry(file_id).or_default(); let diagnostics = self
.check
.entry(flycheck_id)
.or_default()
.entry(package_id.clone())
.or_default()
.entry(file_id)
.or_default();
for existing_diagnostic in diagnostics.iter() { for existing_diagnostic in diagnostics.iter() {
if are_diagnostics_equal(existing_diagnostic, &diagnostic) { if are_diagnostics_equal(existing_diagnostic, &diagnostic) {
return; return;
@ -86,7 +118,14 @@ impl DiagnosticCollection {
if let Some(fix) = fix { if let Some(fix) = fix {
let check_fixes = Arc::make_mut(&mut self.check_fixes); let check_fixes = Arc::make_mut(&mut self.check_fixes);
check_fixes.entry(flycheck_id).or_default().entry(file_id).or_default().push(*fix); check_fixes
.entry(flycheck_id)
.or_default()
.entry(package_id.clone())
.or_default()
.entry(file_id)
.or_default()
.push(*fix);
} }
diagnostics.push(diagnostic); diagnostics.push(diagnostic);
self.changes.insert(file_id); self.changes.insert(file_id);
@ -135,7 +174,12 @@ impl DiagnosticCollection {
) -> impl Iterator<Item = &lsp_types::Diagnostic> { ) -> impl Iterator<Item = &lsp_types::Diagnostic> {
let native_syntax = self.native_syntax.get(&file_id).into_iter().flat_map(|(_, d)| d); let native_syntax = self.native_syntax.get(&file_id).into_iter().flat_map(|(_, d)| d);
let native_semantic = self.native_semantic.get(&file_id).into_iter().flat_map(|(_, d)| d); let native_semantic = self.native_semantic.get(&file_id).into_iter().flat_map(|(_, d)| d);
let check = self.check.values().filter_map(move |it| it.get(&file_id)).flatten(); let check = self
.check
.values()
.flat_map(|it| it.values())
.filter_map(move |it| it.get(&file_id))
.flatten();
native_syntax.chain(native_semantic).chain(check) native_syntax.chain(native_semantic).chain(check)
} }

View file

@ -1,17 +1,20 @@
//! Flycheck provides the functionality needed to run `cargo check` to provide //! Flycheck provides the functionality needed to run `cargo check` to provide
//! LSP diagnostics based on the output of the command. //! LSP diagnostics based on the output of the command.
use std::{fmt, io, process::Command, time::Duration}; use std::{fmt, io, mem, process::Command, time::Duration};
use cargo_metadata::PackageId;
use crossbeam_channel::{select_biased, unbounded, Receiver, Sender}; use crossbeam_channel::{select_biased, unbounded, Receiver, Sender};
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::Deserialize; use serde::Deserialize as _;
use serde_derive::Deserialize;
pub(crate) use cargo_metadata::diagnostic::{ pub(crate) use cargo_metadata::diagnostic::{
Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
}; };
use toolchain::Tool; use toolchain::Tool;
use triomphe::Arc;
use crate::command::{CommandHandle, ParseFromLine}; use crate::command::{CommandHandle, ParseFromLine};
@ -150,10 +153,19 @@ impl FlycheckHandle {
pub(crate) enum FlycheckMessage { pub(crate) enum FlycheckMessage {
/// Request adding a diagnostic with fixes included to a file /// Request adding a diagnostic with fixes included to a file
AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic }, AddDiagnostic {
id: usize,
workspace_root: Arc<AbsPathBuf>,
diagnostic: Diagnostic,
package_id: Option<Arc<PackageId>>,
},
/// Request clearing all previous diagnostics /// Request clearing all outdated diagnostics.
ClearDiagnostics { id: usize }, ClearDiagnostics {
id: usize,
/// The package whose diagnostics to clear, or if unspecified, all diagnostics.
package_id: Option<Arc<PackageId>>,
},
/// Request check progress notification to client /// Request check progress notification to client
Progress { Progress {
@ -166,15 +178,18 @@ pub(crate) enum FlycheckMessage {
impl fmt::Debug for FlycheckMessage { impl fmt::Debug for FlycheckMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => f FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic, package_id } => f
.debug_struct("AddDiagnostic") .debug_struct("AddDiagnostic")
.field("id", id) .field("id", id)
.field("workspace_root", workspace_root) .field("workspace_root", workspace_root)
.field("package_id", package_id)
.field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code)) .field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code))
.finish(), .finish(),
FlycheckMessage::ClearDiagnostics { id } => { FlycheckMessage::ClearDiagnostics { id, package_id } => f
f.debug_struct("ClearDiagnostics").field("id", id).finish() .debug_struct("ClearDiagnostics")
} .field("id", id)
.field("package_id", package_id)
.finish(),
FlycheckMessage::Progress { id, progress } => { FlycheckMessage::Progress { id, progress } => {
f.debug_struct("Progress").field("id", id).field("progress", progress).finish() f.debug_struct("Progress").field("id", id).field("progress", progress).finish()
} }
@ -200,12 +215,13 @@ enum StateChange {
struct FlycheckActor { struct FlycheckActor {
/// The workspace id of this flycheck instance. /// The workspace id of this flycheck instance.
id: usize, id: usize,
sender: Sender<FlycheckMessage>, sender: Sender<FlycheckMessage>,
config: FlycheckConfig, config: FlycheckConfig,
manifest_path: Option<AbsPathBuf>, manifest_path: Option<AbsPathBuf>,
/// Either the workspace root of the workspace we are flychecking, /// Either the workspace root of the workspace we are flychecking,
/// or the project root of the project. /// or the project root of the project.
root: AbsPathBuf, root: Arc<AbsPathBuf>,
sysroot_root: Option<AbsPathBuf>, sysroot_root: Option<AbsPathBuf>,
/// CargoHandle exists to wrap around the communication needed to be able to /// CargoHandle exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library /// run `cargo check` without blocking. Currently the Rust standard library
@ -215,8 +231,13 @@ struct FlycheckActor {
command_handle: Option<CommandHandle<CargoCheckMessage>>, command_handle: Option<CommandHandle<CargoCheckMessage>>,
/// The receiver side of the channel mentioned above. /// The receiver side of the channel mentioned above.
command_receiver: Option<Receiver<CargoCheckMessage>>, command_receiver: Option<Receiver<CargoCheckMessage>>,
package_status: FxHashMap<Arc<PackageId>, DiagnosticReceived>,
}
status: FlycheckStatus, #[derive(PartialEq, Eq, Copy, Clone, Debug)]
enum DiagnosticReceived {
Yes,
No,
} }
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
@ -225,13 +246,6 @@ enum Event {
CheckEvent(Option<CargoCheckMessage>), CheckEvent(Option<CargoCheckMessage>),
} }
#[derive(PartialEq)]
enum FlycheckStatus {
Started,
DiagnosticSent,
Finished,
}
pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file";
impl FlycheckActor { impl FlycheckActor {
@ -249,11 +263,11 @@ impl FlycheckActor {
sender, sender,
config, config,
sysroot_root, sysroot_root,
root: workspace_root, root: Arc::new(workspace_root),
manifest_path, manifest_path,
command_handle: None, command_handle: None,
command_receiver: None, command_receiver: None,
status: FlycheckStatus::Finished, package_status: FxHashMap::default(),
} }
} }
@ -306,13 +320,11 @@ impl FlycheckActor {
self.command_handle = Some(command_handle); self.command_handle = Some(command_handle);
self.command_receiver = Some(receiver); self.command_receiver = Some(receiver);
self.report_progress(Progress::DidStart); self.report_progress(Progress::DidStart);
self.status = FlycheckStatus::Started;
} }
Err(error) => { Err(error) => {
self.report_progress(Progress::DidFailToRestart(format!( self.report_progress(Progress::DidFailToRestart(format!(
"Failed to run the following command: {formatted_command} error={error}" "Failed to run the following command: {formatted_command} error={error}"
))); )));
self.status = FlycheckStatus::Finished;
} }
} }
} }
@ -332,37 +344,62 @@ impl FlycheckActor {
error error
); );
} }
if self.status == FlycheckStatus::Started { if self.package_status.is_empty() {
self.send(FlycheckMessage::ClearDiagnostics { id: self.id }); // We finished without receiving any diagnostics.
// That means all of them are stale.
self.send(FlycheckMessage::ClearDiagnostics {
id: self.id,
package_id: None,
});
} else {
for (package_id, status) in mem::take(&mut self.package_status) {
if let DiagnosticReceived::No = status {
self.send(FlycheckMessage::ClearDiagnostics {
id: self.id,
package_id: Some(package_id),
});
}
}
} }
self.report_progress(Progress::DidFinish(res)); self.report_progress(Progress::DidFinish(res));
self.status = FlycheckStatus::Finished;
} }
Event::CheckEvent(Some(message)) => match message { Event::CheckEvent(Some(message)) => match message {
CargoCheckMessage::CompilerArtifact(msg) => { CargoCheckMessage::CompilerArtifact(msg) => {
tracing::trace!( tracing::trace!(
flycheck_id = self.id, flycheck_id = self.id,
artifact = msg.target.name, artifact = msg.target.name,
package_id = msg.package_id.repr,
"artifact received" "artifact received"
); );
self.report_progress(Progress::DidCheckCrate(msg.target.name)); self.report_progress(Progress::DidCheckCrate(msg.target.name));
self.package_status
.entry(Arc::new(msg.package_id))
.or_insert(DiagnosticReceived::No);
} }
CargoCheckMessage::Diagnostic { diagnostic, package_id } => {
CargoCheckMessage::Diagnostic(msg) => {
tracing::trace!( tracing::trace!(
flycheck_id = self.id, flycheck_id = self.id,
message = msg.message, message = diagnostic.message,
package_id = package_id.as_ref().map(|it| &it.repr),
"diagnostic received" "diagnostic received"
); );
if self.status == FlycheckStatus::Started { if let Some(package_id) = &package_id {
self.send(FlycheckMessage::ClearDiagnostics { id: self.id }); if !self.package_status.contains_key(package_id) {
self.package_status
.insert(package_id.clone(), DiagnosticReceived::Yes);
self.send(FlycheckMessage::ClearDiagnostics {
id: self.id,
package_id: Some(package_id.clone()),
});
}
} }
self.send(FlycheckMessage::AddDiagnostic { self.send(FlycheckMessage::AddDiagnostic {
id: self.id, id: self.id,
package_id,
workspace_root: self.root.clone(), workspace_root: self.root.clone(),
diagnostic: msg, diagnostic,
}); });
self.status = FlycheckStatus::DiagnosticSent;
} }
}, },
} }
@ -380,7 +417,7 @@ impl FlycheckActor {
command_handle.cancel(); command_handle.cancel();
self.command_receiver.take(); self.command_receiver.take();
self.report_progress(Progress::DidCancel); self.report_progress(Progress::DidCancel);
self.status = FlycheckStatus::Finished; self.package_status.clear();
} }
} }
@ -400,7 +437,7 @@ impl FlycheckActor {
cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root)); cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root));
} }
cmd.arg(command); cmd.arg(command);
cmd.current_dir(&self.root); cmd.current_dir(&*self.root);
match package { match package {
Some(pkg) => cmd.arg("-p").arg(pkg), Some(pkg) => cmd.arg("-p").arg(pkg),
@ -442,11 +479,11 @@ impl FlycheckActor {
match invocation_strategy { match invocation_strategy {
InvocationStrategy::Once => { InvocationStrategy::Once => {
cmd.current_dir(&self.root); cmd.current_dir(&*self.root);
} }
InvocationStrategy::PerWorkspace => { InvocationStrategy::PerWorkspace => {
// FIXME: cmd.current_dir(&affected_workspace); // FIXME: cmd.current_dir(&affected_workspace);
cmd.current_dir(&self.root); cmd.current_dir(&*self.root);
} }
} }
@ -486,7 +523,7 @@ impl FlycheckActor {
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
enum CargoCheckMessage { enum CargoCheckMessage {
CompilerArtifact(cargo_metadata::Artifact), CompilerArtifact(cargo_metadata::Artifact),
Diagnostic(Diagnostic), Diagnostic { diagnostic: Diagnostic, package_id: Option<Arc<PackageId>> },
} }
impl ParseFromLine for CargoCheckMessage { impl ParseFromLine for CargoCheckMessage {
@ -501,11 +538,16 @@ impl ParseFromLine for CargoCheckMessage {
Some(CargoCheckMessage::CompilerArtifact(artifact)) Some(CargoCheckMessage::CompilerArtifact(artifact))
} }
cargo_metadata::Message::CompilerMessage(msg) => { cargo_metadata::Message::CompilerMessage(msg) => {
Some(CargoCheckMessage::Diagnostic(msg.message)) Some(CargoCheckMessage::Diagnostic {
diagnostic: msg.message,
package_id: Some(Arc::new(msg.package_id)),
})
} }
_ => None, _ => None,
}, },
JsonMessage::Rustc(message) => Some(CargoCheckMessage::Diagnostic(message)), JsonMessage::Rustc(message) => {
Some(CargoCheckMessage::Diagnostic { diagnostic: message, package_id: None })
}
}; };
} }

View file

@ -92,7 +92,7 @@ pub(crate) struct GlobalState {
// status // status
pub(crate) shutdown_requested: bool, pub(crate) shutdown_requested: bool,
pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>, pub(crate) last_reported_status: lsp_ext::ServerStatusParams,
// proc macros // proc macros
pub(crate) proc_macro_clients: Arc<[anyhow::Result<ProcMacroServer>]>, pub(crate) proc_macro_clients: Arc<[anyhow::Result<ProcMacroServer>]>,
@ -238,7 +238,11 @@ impl GlobalState {
mem_docs: MemDocs::default(), mem_docs: MemDocs::default(),
semantic_tokens_cache: Arc::new(Default::default()), semantic_tokens_cache: Arc::new(Default::default()),
shutdown_requested: false, shutdown_requested: false,
last_reported_status: None, last_reported_status: lsp_ext::ServerStatusParams {
health: lsp_ext::Health::Ok,
quiescent: true,
message: None,
},
source_root_config: SourceRootConfig::default(), source_root_config: SourceRootConfig::default(),
local_roots_parent_map: Arc::new(FxHashMap::default()), local_roots_parent_map: Arc::new(FxHashMap::default()),
config_errors: Default::default(), config_errors: Default::default(),

View file

@ -126,7 +126,7 @@ impl RequestDispatcher<'_> {
/// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
/// ready this will return a `default` constructed [`R::Result`]. /// ready this will return a `default` constructed [`R::Result`].
pub(crate) fn on_with<R>( pub(crate) fn on_with_vfs_default<R>(
&mut self, &mut self,
f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>,
default: impl FnOnce() -> R::Result, default: impl FnOnce() -> R::Result,

View file

@ -189,7 +189,7 @@ pub(crate) fn handle_did_save_text_document(
if !state.config.check_on_save(Some(sr)) || run_flycheck(state, vfs_path) { if !state.config.check_on_save(Some(sr)) || run_flycheck(state, vfs_path) {
return Ok(()); return Ok(());
} }
} else if state.config.check_on_save(None) { } else if state.config.check_on_save(None) && state.config.flycheck_workspace(None) {
// No specific flycheck was triggered, so let's trigger all of them. // No specific flycheck was triggered, so let's trigger all of them.
for flycheck in state.flycheck.iter() { for flycheck in state.flycheck.iter() {
flycheck.restart_workspace(None); flycheck.restart_workspace(None);
@ -293,7 +293,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
let file_id = state.vfs.read().0.file_id(&vfs_path); let file_id = state.vfs.read().0.file_id(&vfs_path);
if let Some(file_id) = file_id { if let Some(file_id) = file_id {
let world = state.snapshot(); let world = state.snapshot();
let source_root_id = world.analysis.source_root_id(file_id).ok(); let may_flycheck_workspace = state.config.flycheck_workspace(None);
let mut updated = false; let mut updated = false;
let task = move || -> std::result::Result<(), ide::Cancelled> { let task = move || -> std::result::Result<(), ide::Cancelled> {
// Is the target binary? If so we let flycheck run only for the workspace that contains the crate. // Is the target binary? If so we let flycheck run only for the workspace that contains the crate.
@ -375,21 +375,22 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
let saved_file = vfs_path.as_path().map(|p| p.to_owned()); let saved_file = vfs_path.as_path().map(|p| p.to_owned());
// Find and trigger corresponding flychecks // Find and trigger corresponding flychecks
for flycheck in world.flycheck.iter() { 'flychecks: for flycheck in world.flycheck.iter() {
for (id, package) in workspace_ids.clone() { for (id, package) in workspace_ids.clone() {
if id == flycheck.id() { if id == flycheck.id() {
updated = true; updated = true;
match package.filter(|_| !world.config.flycheck_workspace(source_root_id)) { if may_flycheck_workspace {
Some(package) => flycheck flycheck.restart_workspace(saved_file.clone())
.restart_for_package(package, target.clone().map(TupleExt::head)), } else if let Some(package) = package {
None => flycheck.restart_workspace(saved_file.clone()), flycheck
.restart_for_package(package, target.clone().map(TupleExt::head))
} }
continue; continue 'flychecks;
} }
} }
} }
// No specific flycheck was triggered, so let's trigger all of them. // No specific flycheck was triggered, so let's trigger all of them.
if !updated { if !updated && may_flycheck_workspace {
for flycheck in world.flycheck.iter() { for flycheck in world.flycheck.iter() {
flycheck.restart_workspace(saved_file.clone()); flycheck.restart_workspace(saved_file.clone());
} }
@ -432,8 +433,10 @@ pub(crate) fn handle_run_flycheck(
} }
} }
// No specific flycheck was triggered, so let's trigger all of them. // No specific flycheck was triggered, so let's trigger all of them.
for flycheck in state.flycheck.iter() { if state.config.flycheck_workspace(None) {
flycheck.restart_workspace(None); for flycheck in state.flycheck.iter() {
flycheck.restart_workspace(None);
}
} }
Ok(()) Ok(())
} }

View file

@ -481,27 +481,28 @@ pub(crate) fn handle_document_diagnostics(
snap: GlobalStateSnapshot, snap: GlobalStateSnapshot,
params: lsp_types::DocumentDiagnosticParams, params: lsp_types::DocumentDiagnosticParams,
) -> anyhow::Result<lsp_types::DocumentDiagnosticReportResult> { ) -> anyhow::Result<lsp_types::DocumentDiagnosticReportResult> {
const EMPTY: lsp_types::DocumentDiagnosticReportResult = let empty = || {
lsp_types::DocumentDiagnosticReportResult::Report( lsp_types::DocumentDiagnosticReportResult::Report(
lsp_types::DocumentDiagnosticReport::Full( lsp_types::DocumentDiagnosticReport::Full(
lsp_types::RelatedFullDocumentDiagnosticReport { lsp_types::RelatedFullDocumentDiagnosticReport {
related_documents: None, related_documents: None,
full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport { full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
result_id: None, result_id: Some("rust-analyzer".to_owned()),
items: vec![], items: vec![],
}, },
}, },
), ),
); )
};
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?; let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
let source_root = snap.analysis.source_root_id(file_id)?; let source_root = snap.analysis.source_root_id(file_id)?;
if !snap.analysis.is_local_source_root(source_root)? { if !snap.analysis.is_local_source_root(source_root)? {
return Ok(EMPTY); return Ok(empty());
} }
let config = snap.config.diagnostics(Some(source_root)); let config = snap.config.diagnostics(Some(source_root));
if !config.enabled { if !config.enabled {
return Ok(EMPTY); return Ok(empty());
} }
let line_index = snap.file_line_index(file_id)?; let line_index = snap.file_line_index(file_id)?;
let supports_related = snap.config.text_document_diagnostic_related_document_support(); let supports_related = snap.config.text_document_diagnostic_related_document_support();
@ -529,7 +530,7 @@ pub(crate) fn handle_document_diagnostics(
Ok(lsp_types::DocumentDiagnosticReportResult::Report( Ok(lsp_types::DocumentDiagnosticReportResult::Report(
lsp_types::DocumentDiagnosticReport::Full(lsp_types::RelatedFullDocumentDiagnosticReport { lsp_types::DocumentDiagnosticReport::Full(lsp_types::RelatedFullDocumentDiagnosticReport {
full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport { full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
result_id: None, result_id: Some("rust-analyzer".to_owned()),
items: diagnostics.collect(), items: diagnostics.collect(),
}, },
related_documents: related_documents.is_empty().not().then(|| { related_documents: related_documents.is_empty().not().then(|| {
@ -539,7 +540,10 @@ pub(crate) fn handle_document_diagnostics(
( (
to_proto::url(&snap, id), to_proto::url(&snap, id),
lsp_types::DocumentDiagnosticReportKind::Full( lsp_types::DocumentDiagnosticReportKind::Full(
lsp_types::FullDocumentDiagnosticReport { result_id: None, items }, lsp_types::FullDocumentDiagnosticReport {
result_id: Some("rust-analyzer".to_owned()),
items,
},
), ),
) )
}) })
@ -1144,7 +1148,7 @@ pub(crate) fn handle_completion_resolve(
let Some(corresponding_completion) = completions.into_iter().find(|completion_item| { let Some(corresponding_completion) = completions.into_iter().find(|completion_item| {
// Avoid computing hashes for items that obviously do not match // Avoid computing hashes for items that obviously do not match
// r-a might append a detail-based suffix to the label, so we cannot check for equality // r-a might append a detail-based suffix to the label, so we cannot check for equality
original_completion.label.starts_with(completion_item.label.as_str()) original_completion.label.starts_with(completion_item.label.primary.as_str())
&& resolve_data_hash == completion_item_hash(completion_item, resolve_data.for_ref) && resolve_data_hash == completion_item_hash(completion_item, resolve_data.for_ref)
}) else { }) else {
return Ok(original_completion); return Ok(original_completion);
@ -1441,7 +1445,13 @@ pub(crate) fn handle_code_action(
} }
// Fixes from `cargo check`. // Fixes from `cargo check`.
for fix in snap.check_fixes.values().filter_map(|it| it.get(&frange.file_id)).flatten() { for fix in snap
.check_fixes
.values()
.flat_map(|it| it.values())
.filter_map(|it| it.get(&frange.file_id))
.flatten()
{
// FIXME: this mapping is awkward and shouldn't exist. Refactor // FIXME: this mapping is awkward and shouldn't exist. Refactor
// `snap.check_fixes` to not convert to LSP prematurely. // `snap.check_fixes` to not convert to LSP prematurely.
let intersect_fix_range = fix let intersect_fix_range = fix

View file

@ -114,8 +114,11 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8;
u8::from(item.deprecated), u8::from(item.deprecated),
u8::from(item.trigger_call_info), u8::from(item.trigger_call_info),
]); ]);
hasher.update(&item.label); hasher.update(&item.label.primary);
if let Some(label_detail) = &item.label_detail { if let Some(label_detail) = &item.label.detail_left {
hasher.update(label_detail);
}
if let Some(label_detail) = &item.label.detail_right {
hasher.update(label_detail); hasher.update(label_detail);
} }
// NB: do not hash edits or source range, as those may change between the time the client sends the resolve request // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request

View file

@ -823,8 +823,11 @@ impl Request for OnTypeFormatting {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct CompletionResolveData { pub struct CompletionResolveData {
pub position: lsp_types::TextDocumentPositionParams, pub position: lsp_types::TextDocumentPositionParams,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub imports: Vec<CompletionImport>, pub imports: Vec<CompletionImport>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub version: Option<i32>, pub version: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub trigger_character: Option<char>, pub trigger_character: Option<char>,
pub for_ref: bool, pub for_ref: bool,
pub hash: String, pub hash: String,
@ -836,6 +839,7 @@ pub struct InlayHintResolveData {
// This is a string instead of a u64 as javascript can't represent u64 fully // This is a string instead of a u64 as javascript can't represent u64 fully
pub hash: String, pub hash: String,
pub resolve_range: lsp_types::Range, pub resolve_range: lsp_types::Range,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub version: Option<i32>, pub version: Option<i32>,
} }

View file

@ -2,6 +2,7 @@
use std::{ use std::{
iter::once, iter::once,
mem, mem,
ops::Not as _,
sync::atomic::{AtomicU32, Ordering}, sync::atomic::{AtomicU32, Ordering},
}; };
@ -353,14 +354,17 @@ fn completion_item(
}; };
let mut lsp_item = lsp_types::CompletionItem { let mut lsp_item = lsp_types::CompletionItem {
label: item.label.to_string(), label: item.label.primary.to_string(),
detail, detail,
filter_text, filter_text,
kind: Some(completion_item_kind(item.kind)), kind: Some(completion_item_kind(item.kind)),
text_edit, text_edit,
additional_text_edits: Some(additional_text_edits), additional_text_edits: additional_text_edits
.is_empty()
.not()
.then_some(additional_text_edits),
documentation, documentation,
deprecated: Some(item.deprecated), deprecated: item.deprecated.then_some(item.deprecated),
tags, tags,
command, command,
insert_text_format, insert_text_format,
@ -368,15 +372,17 @@ fn completion_item(
}; };
if config.completion_label_details_support() { if config.completion_label_details_support() {
let has_label_details =
item.label.detail_left.is_some() || item.label.detail_right.is_some();
if fields_to_resolve.resolve_label_details { if fields_to_resolve.resolve_label_details {
something_to_resolve |= true; something_to_resolve |= has_label_details;
} else { } else if has_label_details {
lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails { lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
detail: item.label_detail.as_ref().map(ToString::to_string), detail: item.label.detail_left.clone(),
description: item.detail.clone(), description: item.label.detail_right.clone(),
}); });
} }
} else if let Some(label_detail) = &item.label_detail { } else if let Some(label_detail) = &item.label.detail_left {
lsp_item.label.push_str(label_detail.as_str()); lsp_item.label.push_str(label_detail.as_str());
} }
@ -1578,22 +1584,26 @@ pub(crate) fn code_lens(
}; };
let lens_config = snap.config.lens(); let lens_config = snap.config.lens();
if lens_config.run && client_commands_config.run_single && has_root {
let command = command::run_single(&r, &title); if has_root {
acc.push(lsp_types::CodeLens { if lens_config.run && client_commands_config.run_single {
range: annotation_range, let command = command::run_single(&r, &title);
command: Some(command), acc.push(lsp_types::CodeLens {
data: None, range: annotation_range,
}) command: Some(command),
} data: None,
if lens_config.debug && can_debug && client_commands_config.debug_single { })
let command = command::debug_single(&r); }
acc.push(lsp_types::CodeLens { if lens_config.debug && can_debug && client_commands_config.debug_single {
range: annotation_range, let command = command::debug_single(&r);
command: Some(command), acc.push(lsp_types::CodeLens {
data: None, range: annotation_range,
}) command: Some(command),
data: None,
})
}
} }
if lens_config.interpret { if lens_config.interpret {
let command = command::interpret_single(&r); let command = command::interpret_single(&r);
acc.push(lsp_types::CodeLens { acc.push(lsp_types::CodeLens {

View file

@ -408,7 +408,10 @@ impl GlobalState {
if self.is_quiescent() { if self.is_quiescent() {
let became_quiescent = !was_quiescent; let became_quiescent = !was_quiescent;
if became_quiescent { if became_quiescent {
if self.config.check_on_save(None) { if self.config.check_on_save(None)
&& self.config.flycheck_workspace(None)
&& !self.fetch_build_data_queue.op_requested()
{
// Project has loaded properly, kick off initial flycheck // Project has loaded properly, kick off initial flycheck
self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None)); self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None));
} }
@ -656,8 +659,8 @@ impl GlobalState {
fn update_status_or_notify(&mut self) { fn update_status_or_notify(&mut self) {
let status = self.current_status(); let status = self.current_status();
if self.last_reported_status.as_ref() != Some(&status) { if self.last_reported_status != status {
self.last_reported_status = Some(status.clone()); self.last_reported_status = status.clone();
if self.config.server_status_notification() { if self.config.server_status_notification() {
self.send_notification::<lsp_ext::ServerStatusNotification>(status); self.send_notification::<lsp_ext::ServerStatusNotification>(status);
@ -715,6 +718,7 @@ impl GlobalState {
error!("FetchWorkspaceError: {e}"); error!("FetchWorkspaceError: {e}");
} }
self.wants_to_switch = Some("fetched workspace".to_owned()); self.wants_to_switch = Some("fetched workspace".to_owned());
self.diagnostics.clear_check_all();
(Progress::End, None) (Progress::End, None)
} }
}; };
@ -956,7 +960,7 @@ impl GlobalState {
fn handle_flycheck_msg(&mut self, message: FlycheckMessage) { fn handle_flycheck_msg(&mut self, message: FlycheckMessage) {
match message { match message {
FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => { FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic, package_id } => {
let snap = self.snapshot(); let snap = self.snapshot();
let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
&self.config.diagnostics_map(None), &self.config.diagnostics_map(None),
@ -968,6 +972,7 @@ impl GlobalState {
match url_to_file_id(&self.vfs.read().0, &diag.url) { match url_to_file_id(&self.vfs.read().0, &diag.url) {
Ok(file_id) => self.diagnostics.add_check_diagnostic( Ok(file_id) => self.diagnostics.add_check_diagnostic(
id, id,
&package_id,
file_id, file_id,
diag.diagnostic, diag.diagnostic,
diag.fix, diag.fix,
@ -981,9 +986,12 @@ impl GlobalState {
}; };
} }
} }
FlycheckMessage::ClearDiagnostics { id, package_id: None } => {
FlycheckMessage::ClearDiagnostics { id } => self.diagnostics.clear_check(id), self.diagnostics.clear_check(id)
}
FlycheckMessage::ClearDiagnostics { id, package_id: Some(package_id) } => {
self.diagnostics.clear_check_for_package(id, package_id)
}
FlycheckMessage::Progress { id, progress } => { FlycheckMessage::Progress { id, progress } => {
let (state, message) = match progress { let (state, message) = match progress {
flycheck::Progress::DidStart => (Progress::Begin, None), flycheck::Progress::DidStart => (Progress::Begin, None),
@ -1090,12 +1098,12 @@ impl GlobalState {
.on_latency_sensitive::<NO_RETRY, lsp_request::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range) .on_latency_sensitive::<NO_RETRY, lsp_request::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)
// FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change. // FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change.
// All other request handlers // All other request handlers
.on_with::<lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, || lsp_types::DocumentDiagnosticReportResult::Report( .on_with_vfs_default::<lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, || lsp_types::DocumentDiagnosticReportResult::Report(
lsp_types::DocumentDiagnosticReport::Full( lsp_types::DocumentDiagnosticReport::Full(
lsp_types::RelatedFullDocumentDiagnosticReport { lsp_types::RelatedFullDocumentDiagnosticReport {
related_documents: None, related_documents: None,
full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport { full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
result_id: None, result_id: Some("rust-analyzer".to_owned()),
items: vec![], items: vec![],
}, },
}, },

View file

@ -70,7 +70,6 @@ impl GlobalState {
/// are ready to do semantic work. /// are ready to do semantic work.
pub(crate) fn is_quiescent(&self) -> bool { pub(crate) fn is_quiescent(&self) -> bool {
self.vfs_done self.vfs_done
&& self.last_reported_status.is_some()
&& !self.fetch_workspaces_queue.op_in_progress() && !self.fetch_workspaces_queue.op_in_progress()
&& !self.fetch_build_data_queue.op_in_progress() && !self.fetch_build_data_queue.op_in_progress()
&& !self.fetch_proc_macros_queue.op_in_progress() && !self.fetch_proc_macros_queue.op_in_progress()

View file

@ -5,7 +5,8 @@ use std::process::Command;
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
use paths::AbsPath; use paths::AbsPath;
use serde::Deserialize; use serde::Deserialize as _;
use serde_derive::Deserialize;
use toolchain::Tool; use toolchain::Tool;
use crate::{ use crate::{

View file

@ -54,7 +54,7 @@ where
fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {} fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {}
fn on_close(&self, id: Id, ctx: Context<'_, S>) { fn on_close(&self, id: Id, ctx: Context<'_, S>) {
#[derive(serde::Serialize)] #[derive(serde_derive::Serialize)]
struct JsonDataInner { struct JsonDataInner {
name: &'static str, name: &'static str,
elapsed_ms: u128, elapsed_ms: u128,

View file

@ -1,7 +1,7 @@
use std::{ use std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
env, fs, env, fs,
sync::{Once, OnceLock}, sync::Once,
time::Duration, time::Duration,
}; };
@ -141,34 +141,15 @@ impl Project<'_> {
/// file in the config dir after server is run, something where our naive approach comes short. /// file in the config dir after server is run, something where our naive approach comes short.
/// Using a `prelock` allows us to force a lock when we know we need it. /// Using a `prelock` allows us to force a lock when we know we need it.
pub(crate) fn server_with_lock(self, config_lock: bool) -> Server { pub(crate) fn server_with_lock(self, config_lock: bool) -> Server {
static CONFIG_DIR_LOCK: OnceLock<(Utf8PathBuf, Mutex<()>)> = OnceLock::new(); static CONFIG_DIR_LOCK: Mutex<()> = Mutex::new(());
let config_dir_guard = if config_lock { let config_dir_guard = if config_lock {
Some({ Some({
let (path, mutex) = CONFIG_DIR_LOCK.get_or_init(|| { let guard = CONFIG_DIR_LOCK.lock();
let value = TestDir::new().keep().path().to_owned(); let test_dir = TestDir::new();
env::set_var("__TEST_RA_USER_CONFIG_DIR", &value); let value = test_dir.path().to_owned();
(value, Mutex::new(())) env::set_var("__TEST_RA_USER_CONFIG_DIR", &value);
}); (guard, test_dir)
#[allow(dyn_drop)]
(mutex.lock(), {
Box::new({
struct Dropper(Utf8PathBuf);
impl Drop for Dropper {
fn drop(&mut self) {
for entry in fs::read_dir(&self.0).unwrap() {
let path = entry.unwrap().path();
if path.is_file() {
fs::remove_file(path).unwrap();
} else if path.is_dir() {
fs::remove_dir_all(path).unwrap();
}
}
}
}
Dropper(path.clone())
}) as Box<dyn Drop>
})
}) })
} else { } else {
None None
@ -311,14 +292,12 @@ pub(crate) struct Server {
client: Connection, client: Connection,
/// XXX: remove the tempdir last /// XXX: remove the tempdir last
dir: TestDir, dir: TestDir,
#[allow(dyn_drop)] _config_dir_guard: Option<(MutexGuard<'static, ()>, TestDir)>,
_config_dir_guard: Option<(MutexGuard<'static, ()>, Box<dyn Drop>)>,
} }
impl Server { impl Server {
#[allow(dyn_drop)]
fn new( fn new(
config_dir_guard: Option<(MutexGuard<'static, ()>, Box<dyn Drop>)>, config_dir_guard: Option<(MutexGuard<'static, ()>, TestDir)>,
dir: TestDir, dir: TestDir,
config: Config, config: Config,
) -> Server { ) -> Server {

View file

@ -12,7 +12,7 @@ authors.workspace = true
[dependencies] [dependencies]
la-arena.workspace = true la-arena.workspace = true
ra-salsa.workspace = true ra-salsa = { workspace = true, optional = true }
rustc-hash.workspace = true rustc-hash.workspace = true
hashbrown.workspace = true hashbrown.workspace = true
text-size.workspace = true text-size.workspace = true
@ -22,5 +22,8 @@ vfs.workspace = true
syntax.workspace = true syntax.workspace = true
stdx.workspace = true stdx.workspace = true
[features]
default = ["ra-salsa"]
[lints] [lints]
workspace = true workspace = true

View file

@ -21,6 +21,9 @@
//! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer. //! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer.
use std::fmt; use std::fmt;
#[cfg(not(feature = "ra-salsa"))]
use crate::InternId;
#[cfg(feature = "ra-salsa")]
use ra_salsa::{InternId, InternValue}; use ra_salsa::{InternId, InternValue};
use crate::MacroCallId; use crate::MacroCallId;
@ -39,6 +42,7 @@ impl fmt::Debug for SyntaxContextId {
} }
} }
#[cfg(feature = "ra-salsa")]
impl ra_salsa::InternKey for SyntaxContextId { impl ra_salsa::InternKey for SyntaxContextId {
fn from_intern_id(v: ra_salsa::InternId) -> Self { fn from_intern_id(v: ra_salsa::InternId) -> Self {
SyntaxContextId(v) SyntaxContextId(v)
@ -92,6 +96,7 @@ pub struct SyntaxContextData {
pub opaque_and_semitransparent: SyntaxContextId, pub opaque_and_semitransparent: SyntaxContextId,
} }
#[cfg(feature = "ra-salsa")]
impl InternValue for SyntaxContextData { impl InternValue for SyntaxContextData {
type Key = (SyntaxContextId, Option<MacroCallId>, Transparency); type Key = (SyntaxContextId, Option<MacroCallId>, Transparency);

Some files were not shown because too many files have changed in this diff Show more