⬆️ rust-analyzer

Merge commit '368e0bb32f1178cf162c2ce5f7e10b7ae211eb26'
This commit is contained in:
arcnmx 2023-01-09 10:36:22 -08:00
parent b3ef934ccb
commit 25242fe93f
395 changed files with 14569 additions and 5755 deletions

89
Cargo.lock generated
View file

@ -112,6 +112,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "camino" name = "camino"
version = "1.1.1" version = "1.1.1"
@ -171,9 +177,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chalk-derive" name = "chalk-derive"
version = "0.86.0" version = "0.88.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5499d415d855b5094366a824815341893ad3de0ecb6048c430118bdae6d27402" checksum = "4df80a3fbc1f0e59f560eeeebca94bf655566a8ad3023c210a109deb6056455a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -183,9 +189,9 @@ dependencies = [
[[package]] [[package]]
name = "chalk-ir" name = "chalk-ir"
version = "0.86.0" version = "0.88.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3800118c76a48507b0eece3a01f3a429b5c478d203c493096e6040c67ab960e1" checksum = "f39e5272016916956298cceea5147006f897972c274a768ed4d6e074efe5d3fb"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"chalk-derive", "chalk-derive",
@ -194,9 +200,9 @@ dependencies = [
[[package]] [[package]]
name = "chalk-recursive" name = "chalk-recursive"
version = "0.86.0" version = "0.88.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1baf60628fd73104d1f8562586a52d48f37f1e84435aab2e62674b1fd935b8c8" checksum = "d9d60b42ad7478d3e027e2f9ea4e99fbbb8fdee0c8c3cf068be269f57e603618"
dependencies = [ dependencies = [
"chalk-derive", "chalk-derive",
"chalk-ir", "chalk-ir",
@ -207,9 +213,9 @@ dependencies = [
[[package]] [[package]]
name = "chalk-solve" name = "chalk-solve"
version = "0.86.0" version = "0.88.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e9c3c068f9358786348e58a1b94ef0a5cf90a9810fc1f10fda896f0b5d80185" checksum = "ab30620ea5b36819525eaab2204f4b8e1842fc7ee36826424a28bef59ae7fecf"
dependencies = [ dependencies = [
"chalk-derive", "chalk-derive",
"chalk-ir", "chalk-ir",
@ -510,6 +516,8 @@ dependencies = [
"fst", "fst",
"hashbrown", "hashbrown",
"hir-expand", "hir-expand",
"hkalbasi-rustc-ap-rustc_abi",
"hkalbasi-rustc-ap-rustc_index",
"indexmap", "indexmap",
"itertools", "itertools",
"la-arena", "la-arena",
@ -555,6 +563,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"base-db", "base-db",
"bitflags",
"chalk-derive", "chalk-derive",
"chalk-ir", "chalk-ir",
"chalk-recursive", "chalk-recursive",
@ -564,6 +573,7 @@ dependencies = [
"expect-test", "expect-test",
"hir-def", "hir-def",
"hir-expand", "hir-expand",
"hkalbasi-rustc-ap-rustc_index",
"itertools", "itertools",
"la-arena", "la-arena",
"limit", "limit",
@ -581,6 +591,27 @@ dependencies = [
"typed-arena", "typed-arena",
] ]
[[package]]
name = "hkalbasi-rustc-ap-rustc_abi"
version = "0.0.20221221"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adabaadad9aa7576f97af02241cdf5554d62fb3d51a84cb05d77ba28edd3013f"
dependencies = [
"bitflags",
"hkalbasi-rustc-ap-rustc_index",
"tracing",
]
[[package]]
name = "hkalbasi-rustc-ap-rustc_index"
version = "0.0.20221221"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4d3c48474e09afb0f5efbd6f758e05411699301a113c47d454d28ec7059d00e"
dependencies = [
"arrayvec",
"smallvec",
]
[[package]] [[package]]
name = "home" name = "home"
version = "0.5.4" version = "0.5.4"
@ -631,6 +662,7 @@ dependencies = [
"ide-db", "ide-db",
"itertools", "itertools",
"profile", "profile",
"smallvec",
"sourcegen", "sourcegen",
"stdx", "stdx",
"syntax", "syntax",
@ -1750,6 +1782,33 @@ dependencies = [
"tikv-jemalloc-sys", "tikv-jemalloc-sys",
] ]
[[package]]
name = "time"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
dependencies = [
"itoa",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "time-macros"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
dependencies = [
"time-core",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.6.0" version = "1.6.0"
@ -2148,4 +2207,18 @@ dependencies = [
"write-json", "write-json",
"xflags", "xflags",
"xshell", "xshell",
"zip",
]
[[package]]
name = "zip"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
"flate2",
"time",
] ]

View file

@ -162,6 +162,7 @@ impl ChangeFixture {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
origin, origin,
meta.target_data_layout.as_deref().map(Arc::from),
); );
let prev = crates.insert(crate_name.clone(), crate_id); let prev = crates.insert(crate_name.clone(), crate_id);
assert!(prev.is_none()); assert!(prev.is_none());
@ -197,6 +198,7 @@ impl ChangeFixture {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
} else { } else {
for (from, to, prelude) in crate_deps { for (from, to, prelude) in crate_deps {
@ -210,6 +212,8 @@ impl ChangeFixture {
.unwrap(); .unwrap();
} }
} }
let target_layout =
crate_graph.iter().next().and_then(|it| crate_graph[it].target_layout.clone());
if let Some(mini_core) = mini_core { if let Some(mini_core) = mini_core {
let core_file = file_id; let core_file = file_id;
@ -234,6 +238,7 @@ impl ChangeFixture {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::Lang(LangCrateOrigin::Core), CrateOrigin::Lang(LangCrateOrigin::Core),
target_layout.clone(),
); );
for krate in all_crates { for krate in all_crates {
@ -271,6 +276,7 @@ impl ChangeFixture {
Ok(proc_macro), Ok(proc_macro),
true, true,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
target_layout,
); );
for krate in all_crates { for krate in all_crates {
@ -391,6 +397,7 @@ struct FileMeta {
edition: Edition, edition: Edition,
env: Env, env: Env,
introduce_new_source_root: Option<SourceRootKind>, introduce_new_source_root: Option<SourceRootKind>,
target_data_layout: Option<String>,
} }
fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) { fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
@ -400,9 +407,9 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
Some((version, url)) => { Some((version, url)) => {
(version, CrateOrigin::CratesIo { repo: Some(url.to_owned()), name: None }) (version, CrateOrigin::CratesIo { repo: Some(url.to_owned()), name: None })
} }
_ => panic!("Bad crates.io parameter: {}", data), _ => panic!("Bad crates.io parameter: {data}"),
}, },
_ => panic!("Bad string for crate origin: {}", b), _ => panic!("Bad string for crate origin: {b}"),
}; };
(a.to_owned(), origin, Some(version.to_string())) (a.to_owned(), origin, Some(version.to_string()))
} else { } else {
@ -432,8 +439,9 @@ impl From<Fixture> for FileMeta {
introduce_new_source_root: f.introduce_new_source_root.map(|kind| match &*kind { introduce_new_source_root: f.introduce_new_source_root.map(|kind| match &*kind {
"local" => SourceRootKind::Local, "local" => SourceRootKind::Local,
"library" => SourceRootKind::Library, "library" => SourceRootKind::Library,
invalid => panic!("invalid source root kind '{}'", invalid), invalid => panic!("invalid source root kind '{invalid}'"),
}), }),
target_data_layout: f.target_data_layout,
} }
} }
} }

View file

@ -128,7 +128,7 @@ impl fmt::Display for CrateName {
impl ops::Deref for CrateName { impl ops::Deref for CrateName {
type Target = str; type Target = str;
fn deref(&self) -> &str { fn deref(&self) -> &str {
&*self.0 &self.0
} }
} }
@ -211,7 +211,7 @@ impl fmt::Display for CrateDisplayName {
impl ops::Deref for CrateDisplayName { impl ops::Deref for CrateDisplayName {
type Target = str; type Target = str;
fn deref(&self) -> &str { fn deref(&self) -> &str {
&*self.crate_name &self.crate_name
} }
} }
@ -270,6 +270,7 @@ pub struct CrateData {
pub display_name: Option<CrateDisplayName>, pub display_name: Option<CrateDisplayName>,
pub cfg_options: CfgOptions, pub cfg_options: CfgOptions,
pub potential_cfg_options: CfgOptions, pub potential_cfg_options: CfgOptions,
pub target_layout: Option<Arc<str>>,
pub env: Env, pub env: Env,
pub dependencies: Vec<Dependency>, pub dependencies: Vec<Dependency>,
pub proc_macro: ProcMacroLoadResult, pub proc_macro: ProcMacroLoadResult,
@ -328,6 +329,7 @@ impl CrateGraph {
proc_macro: ProcMacroLoadResult, proc_macro: ProcMacroLoadResult,
is_proc_macro: bool, is_proc_macro: bool,
origin: CrateOrigin, origin: CrateOrigin,
target_layout: Option<Arc<str>>,
) -> CrateId { ) -> CrateId {
let data = CrateData { let data = CrateData {
root_file_id, root_file_id,
@ -340,6 +342,7 @@ impl CrateGraph {
proc_macro, proc_macro,
dependencies: Vec::new(), dependencies: Vec::new(),
origin, origin,
target_layout,
is_proc_macro, is_proc_macro,
}; };
let crate_id = CrateId(self.arena.len() as u32); let crate_id = CrateId(self.arena.len() as u32);
@ -615,8 +618,8 @@ impl CyclicDependenciesError {
impl fmt::Display for CyclicDependenciesError { impl fmt::Display for CyclicDependenciesError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let render = |(id, name): &(CrateId, Option<CrateDisplayName>)| match name { let render = |(id, name): &(CrateId, Option<CrateDisplayName>)| match name {
Some(it) => format!("{}({:?})", it, id), Some(it) => format!("{it}({id:?})"),
None => format!("{:?}", id), None => format!("{id:?}"),
}; };
let path = self.path.iter().rev().map(render).collect::<Vec<String>>().join(" -> "); let path = self.path.iter().rev().map(render).collect::<Vec<String>>().join(" -> ");
write!( write!(
@ -649,6 +652,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
let crate2 = graph.add_crate_root( let crate2 = graph.add_crate_root(
FileId(2u32), FileId(2u32),
@ -661,6 +665,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
let crate3 = graph.add_crate_root( let crate3 = graph.add_crate_root(
FileId(3u32), FileId(3u32),
@ -673,6 +678,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
assert!(graph assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@ -699,6 +705,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
let crate2 = graph.add_crate_root( let crate2 = graph.add_crate_root(
FileId(2u32), FileId(2u32),
@ -711,6 +718,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
assert!(graph assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@ -734,6 +742,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
let crate2 = graph.add_crate_root( let crate2 = graph.add_crate_root(
FileId(2u32), FileId(2u32),
@ -746,6 +755,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
let crate3 = graph.add_crate_root( let crate3 = graph.add_crate_root(
FileId(3u32), FileId(3u32),
@ -758,6 +768,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
assert!(graph assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@ -781,6 +792,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
let crate2 = graph.add_crate_root( let crate2 = graph.add_crate_root(
FileId(2u32), FileId(2u32),
@ -793,6 +805,7 @@ mod tests {
Ok(Vec::new()), Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::CratesIo { repo: None, name: None },
None,
); );
assert!(graph assert!(graph
.add_dep( .add_dep(

View file

@ -75,9 +75,9 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug {
} }
fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
let _p = profile::span("parse_query").detail(|| format!("{:?}", file_id)); let _p = profile::span("parse_query").detail(|| format!("{file_id:?}"));
let text = db.file_text(file_id); let text = db.file_text(file_id);
SourceFile::parse(&*text) SourceFile::parse(&text)
} }
/// We don't want to give HIR knowledge of source roots, hence we extract these /// We don't want to give HIR knowledge of source roots, hence we extract these

View file

@ -44,7 +44,7 @@ impl fmt::Display for CfgAtom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
CfgAtom::Flag(name) => name.fmt(f), CfgAtom::Flag(name) => name.fmt(f),
CfgAtom::KeyValue { key, value } => write!(f, "{} = {:?}", key, value), CfgAtom::KeyValue { key, value } => write!(f, "{key} = {value:?}"),
} }
} }
} }

View file

@ -37,7 +37,7 @@ impl fmt::Debug for CfgOptions {
.iter() .iter()
.map(|atom| match atom { .map(|atom| match atom {
CfgAtom::Flag(it) => it.to_string(), CfgAtom::Flag(it) => it.to_string(),
CfgAtom::KeyValue { key, value } => format!("{}={}", key, value), CfgAtom::KeyValue { key, value } => format!("{key}={value}"),
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
items.sort(); items.sort();
@ -175,7 +175,7 @@ impl fmt::Display for InactiveReason {
atom.fmt(f)?; atom.fmt(f)?;
} }
let is_are = if self.enabled.len() == 1 { "is" } else { "are" }; let is_are = if self.enabled.len() == 1 { "is" } else { "are" };
write!(f, " {} enabled", is_are)?; write!(f, " {is_are} enabled")?;
if !self.disabled.is_empty() { if !self.disabled.is_empty() {
f.write_str(" and ")?; f.write_str(" and ")?;
@ -194,7 +194,7 @@ impl fmt::Display for InactiveReason {
atom.fmt(f)?; atom.fmt(f)?;
} }
let is_are = if self.disabled.len() == 1 { "is" } else { "are" }; let is_are = if self.disabled.len() == 1 { "is" } else { "are" };
write!(f, " {} disabled", is_are)?; write!(f, " {is_are} disabled")?;
} }
Ok(()) Ok(())

View file

@ -47,6 +47,7 @@ pub enum FlycheckConfig {
features: Vec<String>, features: Vec<String>,
extra_args: Vec<String>, extra_args: Vec<String>,
extra_env: FxHashMap<String, String>, extra_env: FxHashMap<String, String>,
ansi_color_output: bool,
}, },
CustomCommand { CustomCommand {
command: String, command: String,
@ -60,9 +61,9 @@ pub enum FlycheckConfig {
impl fmt::Display for FlycheckConfig { impl fmt::Display for FlycheckConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command), FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {command}"),
FlycheckConfig::CustomCommand { command, args, .. } => { FlycheckConfig::CustomCommand { command, args, .. } => {
write!(f, "{} {}", command, args.join(" ")) write!(f, "{command} {}", args.join(" "))
} }
} }
} }
@ -293,15 +294,24 @@ impl FlycheckActor {
extra_args, extra_args,
features, features,
extra_env, extra_env,
ansi_color_output,
} => { } => {
let mut cmd = Command::new(toolchain::cargo()); let mut cmd = Command::new(toolchain::cargo());
cmd.arg(command); cmd.arg(command);
cmd.current_dir(&self.root); cmd.current_dir(&self.root);
cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) cmd.arg("--workspace");
.arg(self.root.join("Cargo.toml").as_os_str());
cmd.arg(if *ansi_color_output {
"--message-format=json-diagnostic-rendered-ansi"
} else {
"--message-format=json"
});
cmd.arg("--manifest-path");
cmd.arg(self.root.join("Cargo.toml").as_os_str());
for target in target_triples { for target in target_triples {
cmd.args(&["--target", target.as_str()]); cmd.args(["--target", target.as_str()]);
} }
if *all_targets { if *all_targets {
cmd.arg("--all-targets"); cmd.arg("--all-targets");
@ -360,13 +370,20 @@ impl FlycheckActor {
} }
} }
struct JodChild(GroupChild); struct JodGroupChild(GroupChild);
impl Drop for JodGroupChild {
fn drop(&mut self) {
_ = self.0.kill();
_ = self.0.wait();
}
}
/// A handle to a cargo process used for fly-checking. /// A handle to a cargo process used for fly-checking.
struct CargoHandle { struct CargoHandle {
/// The handle to the actual cargo process. As we cannot cancel directly from with /// The handle to the actual cargo process. As we cannot cancel directly from with
/// a read syscall dropping and therefore terminating the process is our best option. /// a read syscall dropping and therefore terminating the process is our best option.
child: JodChild, child: JodGroupChild,
thread: jod_thread::JoinHandle<io::Result<(bool, String)>>, thread: jod_thread::JoinHandle<io::Result<(bool, String)>>,
receiver: Receiver<CargoMessage>, receiver: Receiver<CargoMessage>,
} }
@ -374,7 +391,7 @@ struct CargoHandle {
impl CargoHandle { impl CargoHandle {
fn spawn(mut command: Command) -> std::io::Result<CargoHandle> { fn spawn(mut command: Command) -> std::io::Result<CargoHandle> {
command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
let mut child = command.group_spawn().map(JodChild)?; let mut child = command.group_spawn().map(JodGroupChild)?;
let stdout = child.0.inner().stdout.take().unwrap(); let stdout = child.0.inner().stdout.take().unwrap();
let stderr = child.0.inner().stderr.take().unwrap(); let stderr = child.0.inner().stderr.take().unwrap();
@ -401,8 +418,7 @@ impl CargoHandle {
Ok(()) Ok(())
} else { } else {
Err(io::Error::new(io::ErrorKind::Other, format!( Err(io::Error::new(io::ErrorKind::Other, format!(
"Cargo watcher failed, the command produced no valid metadata (exit code: {:?}):\n{}", "Cargo watcher failed, the command produced no valid metadata (exit code: {exit_status:?}):\n{error}"
exit_status, error
))) )))
} }
} }
@ -467,7 +483,7 @@ impl CargoActor {
); );
match output { match output {
Ok(_) => Ok((read_at_least_one_message, error)), Ok(_) => Ok((read_at_least_one_message, error)),
Err(e) => Err(io::Error::new(e.kind(), format!("{:?}: {}", e, error))), Err(e) => Err(io::Error::new(e.kind(), format!("{e:?}: {error}"))),
} }
} }
} }

View file

@ -33,6 +33,8 @@ base-db = { path = "../base-db", version = "0.0.0" }
syntax = { path = "../syntax", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" }
profile = { path = "../profile", version = "0.0.0" } profile = { path = "../profile", version = "0.0.0" }
hir-expand = { path = "../hir-expand", version = "0.0.0" } hir-expand = { path = "../hir-expand", version = "0.0.0" }
rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
mbe = { path = "../mbe", version = "0.0.0" } mbe = { path = "../mbe", version = "0.0.0" }
cfg = { path = "../cfg", version = "0.0.0" } cfg = { path = "../cfg", version = "0.0.0" }
tt = { path = "../tt", version = "0.0.0" } tt = { path = "../tt", version = "0.0.0" }

View file

@ -1,6 +1,6 @@
//! Defines hir-level representation of structs, enums and unions //! Defines hir-level representation of structs, enums and unions
use std::{num::NonZeroU32, sync::Arc}; use std::sync::Arc;
use base_db::CrateId; use base_db::CrateId;
use either::Either; use either::Either;
@ -9,6 +9,7 @@ use hir_expand::{
HirFileId, InFile, HirFileId, InFile,
}; };
use la_arena::{Arena, ArenaMap}; use la_arena::{Arena, ArenaMap};
use rustc_abi::{Integer, IntegerType};
use syntax::ast::{self, HasName, HasVisibility}; use syntax::ast::{self, HasName, HasVisibility};
use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
@ -18,6 +19,7 @@ use crate::{
db::DefDatabase, db::DefDatabase,
intern::Interned, intern::Interned,
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
layout::{Align, ReprFlags, ReprOptions},
nameres::diagnostics::DefDiagnostic, nameres::diagnostics::DefDiagnostic,
src::HasChildSource, src::HasChildSource,
src::HasSource, src::HasSource,
@ -34,16 +36,18 @@ use cfg::CfgOptions;
pub struct StructData { pub struct StructData {
pub name: Name, pub name: Name,
pub variant_data: Arc<VariantData>, pub variant_data: Arc<VariantData>,
pub repr: Option<ReprData>, pub repr: Option<ReprOptions>,
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub rustc_has_incoherent_inherent_impls: bool,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData { pub struct EnumData {
pub name: Name, pub name: Name,
pub variants: Arena<EnumVariantData>, pub variants: Arena<EnumVariantData>,
pub repr: Option<ReprData>, pub repr: Option<ReprOptions>,
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub rustc_has_incoherent_inherent_impls: bool,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -67,80 +71,91 @@ pub struct FieldData {
pub visibility: RawVisibility, pub visibility: RawVisibility,
} }
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub enum ReprKind {
C,
BuiltinInt { builtin: Either<BuiltinInt, BuiltinUint>, is_c: bool },
Transparent,
Default,
}
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub struct ReprData {
pub kind: ReprKind,
pub packed: bool,
pub align: Option<NonZeroU32>,
}
fn repr_from_value( fn repr_from_value(
db: &dyn DefDatabase, db: &dyn DefDatabase,
krate: CrateId, krate: CrateId,
item_tree: &ItemTree, item_tree: &ItemTree,
of: AttrOwner, of: AttrOwner,
) -> Option<ReprData> { ) -> Option<ReprOptions> {
item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt) item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
} }
fn parse_repr_tt(tt: &Subtree) -> Option<ReprData> { fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
match tt.delimiter { match tt.delimiter {
Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {} Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
_ => return None, _ => return None,
} }
let mut data = ReprData { kind: ReprKind::Default, packed: false, align: None }; let mut flags = ReprFlags::empty();
let mut int = None;
let mut max_align: Option<Align> = None;
let mut min_pack: Option<Align> = None;
let mut tts = tt.token_trees.iter().peekable(); let mut tts = tt.token_trees.iter().peekable();
while let Some(tt) = tts.next() { while let Some(tt) = tts.next() {
if let TokenTree::Leaf(Leaf::Ident(ident)) = tt { if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
match &*ident.text { flags.insert(match &*ident.text {
"packed" => { "packed" => {
data.packed = true; let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() {
if let Some(TokenTree::Subtree(_)) = tts.peek() {
tts.next(); tts.next();
} if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
lit.text.parse().unwrap_or_default()
} else {
0
}
} else {
0
};
let pack = Align::from_bytes(pack).unwrap();
min_pack =
Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack });
ReprFlags::empty()
} }
"align" => { "align" => {
if let Some(TokenTree::Subtree(tt)) = tts.peek() { if let Some(TokenTree::Subtree(tt)) = tts.peek() {
tts.next(); tts.next();
if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() { if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
if let Ok(align) = lit.text.parse() { if let Ok(align) = lit.text.parse() {
data.align = Some(align); let align = Align::from_bytes(align).ok();
max_align = max_align.max(align);
} }
} }
} }
ReprFlags::empty()
} }
"C" => { "C" => ReprFlags::IS_C,
if let ReprKind::BuiltinInt { is_c, .. } = &mut data.kind { "transparent" => ReprFlags::IS_TRANSPARENT,
*is_c = true;
} else {
data.kind = ReprKind::C;
}
}
"transparent" => data.kind = ReprKind::Transparent,
repr => { repr => {
let is_c = matches!(data.kind, ReprKind::C);
if let Some(builtin) = BuiltinInt::from_suffix(repr) if let Some(builtin) = BuiltinInt::from_suffix(repr)
.map(Either::Left) .map(Either::Left)
.or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right)) .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
{ {
data.kind = ReprKind::BuiltinInt { builtin, is_c }; int = Some(match builtin {
Either::Left(bi) => match bi {
BuiltinInt::Isize => IntegerType::Pointer(true),
BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
},
Either::Right(bu) => match bu {
BuiltinUint::Usize => IntegerType::Pointer(false),
BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
},
});
} }
ReprFlags::empty()
} }
} })
} }
} }
Some(data) Some(ReprOptions { int, align: max_align, pack: min_pack, flags, field_shuffle_seed: 0 })
} }
impl StructData { impl StructData {
@ -157,6 +172,10 @@ impl StructData {
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
.by_key("rustc_has_incoherent_inherent_impls")
.exists();
let strukt = &item_tree[loc.id.value]; let strukt = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields( let (variant_data, diagnostics) = lower_fields(
@ -175,6 +194,7 @@ impl StructData {
variant_data: Arc::new(variant_data), variant_data: Arc::new(variant_data),
repr, repr,
visibility: item_tree[strukt.visibility].clone(), visibility: item_tree[strukt.visibility].clone(),
rustc_has_incoherent_inherent_impls,
}), }),
diagnostics.into(), diagnostics.into(),
) )
@ -194,6 +214,11 @@ impl StructData {
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
.by_key("rustc_has_incoherent_inherent_impls")
.exists();
let union = &item_tree[loc.id.value]; let union = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields( let (variant_data, diagnostics) = lower_fields(
db, db,
@ -211,6 +236,7 @@ impl StructData {
variant_data: Arc::new(variant_data), variant_data: Arc::new(variant_data),
repr, repr,
visibility: item_tree[union.visibility].clone(), visibility: item_tree[union.visibility].clone(),
rustc_has_incoherent_inherent_impls,
}), }),
diagnostics.into(), diagnostics.into(),
) )
@ -231,6 +257,10 @@ impl EnumData {
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let cfg_options = db.crate_graph()[krate].cfg_options.clone(); let cfg_options = db.crate_graph()[krate].cfg_options.clone();
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
.by_key("rustc_has_incoherent_inherent_impls")
.exists();
let enum_ = &item_tree[loc.id.value]; let enum_ = &item_tree[loc.id.value];
let mut variants = Arena::new(); let mut variants = Arena::new();
@ -271,6 +301,7 @@ impl EnumData {
variants, variants,
repr, repr,
visibility: item_tree[enum_.visibility].clone(), visibility: item_tree[enum_.visibility].clone(),
rustc_has_incoherent_inherent_impls,
}), }),
diagnostics.into(), diagnostics.into(),
) )
@ -281,10 +312,10 @@ impl EnumData {
Some(id) Some(id)
} }
pub fn variant_body_type(&self) -> Either<BuiltinInt, BuiltinUint> { pub fn variant_body_type(&self) -> IntegerType {
match self.repr { match self.repr {
Some(ReprData { kind: ReprKind::BuiltinInt { builtin, .. }, .. }) => builtin, Some(ReprOptions { int: Some(builtin), .. }) => builtin,
_ => Either::Left(BuiltinInt::Isize), _ => IntegerType::Pointer(true),
} }
} }
} }

View file

@ -712,7 +712,7 @@ impl AttrSourceMap {
self.source self.source
.get(ast_idx) .get(ast_idx)
.map(|it| InFile::new(file_id, it)) .map(|it| InFile::new(file_id, it))
.unwrap_or_else(|| panic!("cannot find attr at index {:?}", id)) .unwrap_or_else(|| panic!("cannot find attr at index {id:?}"))
} }
} }

View file

@ -372,7 +372,7 @@ impl Body {
/// Retrieves all ident patterns this pattern shares the ident with. /// Retrieves all ident patterns this pattern shares the ident with.
pub fn ident_patterns_for<'slf>(&'slf self, pat: &'slf PatId) -> &'slf [PatId] { pub fn ident_patterns_for<'slf>(&'slf self, pat: &'slf PatId) -> &'slf [PatId] {
match self.or_pats.get(pat) { match self.or_pats.get(pat) {
Some(pats) => &**pats, Some(pats) => pats,
None => std::slice::from_ref(pat), None => std::slice::from_ref(pat),
} }
} }

View file

@ -371,6 +371,10 @@ impl ExprCollector<'_> {
let expr = e.expr().map(|e| self.collect_expr(e)); let expr = e.expr().map(|e| self.collect_expr(e));
self.alloc_expr(Expr::Yield { expr }, syntax_ptr) self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
} }
ast::Expr::YeetExpr(e) => {
let expr = e.expr().map(|e| self.collect_expr(e));
self.alloc_expr(Expr::Yeet { expr }, syntax_ptr)
}
ast::Expr::RecordExpr(e) => { ast::Expr::RecordExpr(e) => {
let path = let path =
e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);

View file

@ -32,7 +32,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
Some(name) => name.to_string(), Some(name) => name.to_string(),
None => "_".to_string(), None => "_".to_string(),
}; };
format!("const {} = ", name) format!("const {name} = ")
} }
DefWithBodyId::VariantId(it) => { DefWithBodyId::VariantId(it) => {
needs_semi = false; needs_semi = false;
@ -42,7 +42,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
Some(name) => name.to_string(), Some(name) => name.to_string(),
None => "_".to_string(), None => "_".to_string(),
}; };
format!("{}", name) format!("{name}")
} }
}; };
@ -247,6 +247,15 @@ impl<'a> Printer<'a> {
self.print_expr(*expr); self.print_expr(*expr);
} }
} }
Expr::Yeet { expr } => {
w!(self, "do");
self.whitespace();
w!(self, "yeet");
if let Some(expr) = expr {
self.whitespace();
self.print_expr(*expr);
}
}
Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => { Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => {
match path { match path {
Some(path) => self.print_path(path), Some(path) => self.print_path(path),

View file

@ -47,7 +47,7 @@ pub struct ScopeData {
impl ExprScopes { impl ExprScopes {
pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> { pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
let body = db.body(def); let body = db.body(def);
let mut scopes = ExprScopes::new(&*body); let mut scopes = ExprScopes::new(&body);
scopes.shrink_to_fit(); scopes.shrink_to_fit();
Arc::new(scopes) Arc::new(scopes)
} }

View file

@ -350,6 +350,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
), ),
ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
ungated!(rustc_safe_intrinsic, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
gated!( gated!(
allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk,
"allow_internal_unstable side-steps feature gating and stability checks", "allow_internal_unstable side-steps feature gating and stability checks",

View file

@ -13,7 +13,9 @@ use crate::{
intern::Interned, intern::Interned,
item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId}, item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
nameres::{ nameres::{
attr_resolution::ResolvedAttr, diagnostics::DefDiagnostic, proc_macro::ProcMacroKind, attr_resolution::ResolvedAttr,
diagnostics::DefDiagnostic,
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
DefMap, DefMap,
}, },
type_ref::{TraitRef, TypeBound, TypeRef}, type_ref::{TraitRef, TypeBound, TypeRef},
@ -168,6 +170,7 @@ pub struct TypeAliasData {
pub type_ref: Option<Interned<TypeRef>>, pub type_ref: Option<Interned<TypeRef>>,
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub is_extern: bool, pub is_extern: bool,
pub rustc_has_incoherent_inherent_impls: bool,
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
pub bounds: Vec<Interned<TypeBound>>, pub bounds: Vec<Interned<TypeBound>>,
} }
@ -186,11 +189,17 @@ impl TypeAliasData {
item_tree[typ.visibility].clone() item_tree[typ.visibility].clone()
}; };
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into())
.by_key("rustc_has_incoherent_inherent_impls")
.exists();
Arc::new(TypeAliasData { Arc::new(TypeAliasData {
name: typ.name.clone(), name: typ.name.clone(),
type_ref: typ.type_ref.clone(), type_ref: typ.type_ref.clone(),
visibility, visibility,
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)), is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
rustc_has_incoherent_inherent_impls,
bounds: typ.bounds.to_vec(), bounds: typ.bounds.to_vec(),
}) })
} }
@ -202,6 +211,7 @@ pub struct TraitData {
pub items: Vec<(Name, AssocItemId)>, pub items: Vec<(Name, AssocItemId)>,
pub is_auto: bool, pub is_auto: bool,
pub is_unsafe: bool, pub is_unsafe: bool,
pub rustc_has_incoherent_inherent_impls: bool,
pub visibility: RawVisibility, pub visibility: RawVisibility,
/// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
/// method calls to this trait's methods when the receiver is an array and the crate edition is /// method calls to this trait's methods when the receiver is an array and the crate edition is
@ -224,18 +234,17 @@ impl TraitData {
let item_tree = tree_id.item_tree(db); let item_tree = tree_id.item_tree(db);
let tr_def = &item_tree[tree_id.value]; let tr_def = &item_tree[tree_id.value];
let _cx = stdx::panic_context::enter(format!( let _cx = stdx::panic_context::enter(format!(
"trait_data_query({:?} -> {:?} -> {:?})", "trait_data_query({tr:?} -> {tr_loc:?} -> {tr_def:?})"
tr, tr_loc, tr_def
)); ));
let name = tr_def.name.clone(); let name = tr_def.name.clone();
let is_auto = tr_def.is_auto; let is_auto = tr_def.is_auto;
let is_unsafe = tr_def.is_unsafe; let is_unsafe = tr_def.is_unsafe;
let visibility = item_tree[tr_def.visibility].clone(); let visibility = item_tree[tr_def.visibility].clone();
let skip_array_during_method_dispatch = item_tree let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into());
.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into()) let skip_array_during_method_dispatch =
.by_key("rustc_skip_array_during_method_dispatch") attrs.by_key("rustc_skip_array_during_method_dispatch").exists();
.exists(); let rustc_has_incoherent_inherent_impls =
attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
let (items, attribute_calls, diagnostics) = match &tr_def.items { let (items, attribute_calls, diagnostics) = match &tr_def.items {
Some(items) => { Some(items) => {
let mut collector = AssocItemCollector::new( let mut collector = AssocItemCollector::new(
@ -258,6 +267,7 @@ impl TraitData {
is_unsafe, is_unsafe,
visibility, visibility,
skip_array_during_method_dispatch, skip_array_during_method_dispatch,
rustc_has_incoherent_inherent_impls,
}), }),
diagnostics.into(), diagnostics.into(),
) )
@ -339,6 +349,10 @@ impl ImplData {
pub struct Macro2Data { pub struct Macro2Data {
pub name: Name, pub name: Name,
pub visibility: RawVisibility, pub visibility: RawVisibility,
// It's a bit wasteful as currently this is only for builtin `Default` derive macro, but macro2
// are rarely used in practice so I think it's okay for now.
/// Derive helpers, if this is a derive rustc_builtin_macro
pub helpers: Option<Box<[Name]>>,
} }
impl Macro2Data { impl Macro2Data {
@ -347,9 +361,18 @@ impl Macro2Data {
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let makro = &item_tree[loc.id.value]; let makro = &item_tree[loc.id.value];
let helpers = item_tree
.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
.by_key("rustc_builtin_macro")
.tt_values()
.next()
.and_then(|attr| parse_macro_name_and_helper_attrs(&attr.token_trees))
.map(|(_, helpers)| helpers);
Arc::new(Macro2Data { Arc::new(Macro2Data {
name: makro.name.clone(), name: makro.name.clone(),
visibility: item_tree[makro.visibility].clone(), visibility: item_tree[makro.visibility].clone(),
helpers,
}) })
} }
} }
@ -519,7 +542,7 @@ impl<'a> AssocItemCollector<'a> {
if !attrs.is_cfg_enabled(self.expander.cfg_options()) { if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
self.inactive_diagnostics.push(DefDiagnostic::unconfigured_code( self.inactive_diagnostics.push(DefDiagnostic::unconfigured_code(
self.module_id.local_id, self.module_id.local_id,
InFile::new(self.expander.current_file_id(), item.ast_id(&item_tree).upcast()), InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()),
attrs.cfg().unwrap(), attrs.cfg().unwrap(),
self.expander.cfg_options().clone(), self.expander.cfg_options().clone(),
)); ));
@ -528,7 +551,7 @@ impl<'a> AssocItemCollector<'a> {
'attrs: for attr in &*attrs { 'attrs: for attr in &*attrs {
let ast_id = let ast_id =
AstId::new(self.expander.current_file_id(), item.ast_id(&item_tree).upcast()); AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast());
let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id }; let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id };
if let Ok(ResolvedAttr::Macro(call_id)) = self.def_map.resolve_attr_macro( if let Ok(ResolvedAttr::Macro(call_id)) = self.def_map.resolve_attr_macro(
@ -595,10 +618,8 @@ impl<'a> AssocItemCollector<'a> {
let ast_id_map = self.db.ast_id_map(self.expander.current_file_id()); let ast_id_map = self.db.ast_id_map(self.expander.current_file_id());
let call = ast_id_map.get(call.ast_id).to_node(&root); let call = ast_id_map.get(call.ast_id).to_node(&root);
let _cx = stdx::panic_context::enter(format!( let _cx =
"collect_items MacroCall: {}", stdx::panic_context::enter(format!("collect_items MacroCall: {call}"));
call
));
let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call); let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call);
if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res { if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res {

View file

@ -36,6 +36,13 @@ pub(crate) fn dummy_expr_id() -> ExprId {
pub type PatId = Idx<Pat>; pub type PatId = Idx<Pat>;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum ExprOrPatId {
ExprId(ExprId),
PatId(PatId),
}
stdx::impl_from!(ExprId, PatId for ExprOrPatId);
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct Label { pub struct Label {
pub name: Name, pub name: Name,
@ -137,6 +144,9 @@ pub enum Expr {
Yield { Yield {
expr: Option<ExprId>, expr: Option<ExprId>,
}, },
Yeet {
expr: Option<ExprId>,
},
RecordLit { RecordLit {
path: Option<Box<Path>>, path: Option<Box<Path>>,
fields: Box<[RecordLitField]>, fields: Box<[RecordLitField]>,
@ -313,7 +323,10 @@ impl Expr {
arms.iter().map(|arm| arm.expr).for_each(f); arms.iter().map(|arm| arm.expr).for_each(f);
} }
Expr::Continue { .. } => {} Expr::Continue { .. } => {}
Expr::Break { expr, .. } | Expr::Return { expr } | Expr::Yield { expr } => { Expr::Break { expr, .. }
| Expr::Return { expr }
| Expr::Yield { expr }
| Expr::Yeet { expr } => {
if let &Some(expr) = expr { if let &Some(expr) = expr {
f(expr); f(expr);
} }

View file

@ -107,7 +107,7 @@ fn find_path_inner(
} }
// - if the item is in the prelude, return the name from there // - if the item is in the prelude, return the name from there
if let Some(value) = find_in_prelude(db, &crate_root.def_map(db), item, from) { if let value @ Some(_) = find_in_prelude(db, &crate_root.def_map(db), &def_map, item, from) {
return value; return value;
} }
@ -176,7 +176,7 @@ fn find_path_for_module(
// - if relative paths are fine, check if we are searching for a parent // - if relative paths are fine, check if we are searching for a parent
if prefixed.filter(PrefixKind::is_absolute).is_none() { if prefixed.filter(PrefixKind::is_absolute).is_none() {
if let modpath @ Some(_) = find_self_super(&def_map, module_id, from) { if let modpath @ Some(_) = find_self_super(def_map, module_id, from) {
return modpath; return modpath;
} }
} }
@ -205,7 +205,8 @@ fn find_path_for_module(
} }
} }
if let Some(value) = find_in_prelude(db, &root_def_map, ItemInNs::Types(module_id.into()), from) if let value @ Some(_) =
find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from)
{ {
return value; return value;
} }
@ -234,23 +235,41 @@ fn find_in_scope(
}) })
} }
/// Returns single-segment path (i.e. without any prefix) if `item` is found in prelude and its
/// name doesn't clash in current scope.
fn find_in_prelude( fn find_in_prelude(
db: &dyn DefDatabase, db: &dyn DefDatabase,
root_def_map: &DefMap, root_def_map: &DefMap,
local_def_map: &DefMap,
item: ItemInNs, item: ItemInNs,
from: ModuleId, from: ModuleId,
) -> Option<Option<ModPath>> { ) -> Option<ModPath> {
if let Some(prelude_module) = root_def_map.prelude() { let prelude_module = root_def_map.prelude()?;
// Preludes in block DefMaps are ignored, only the crate DefMap is searched // Preludes in block DefMaps are ignored, only the crate DefMap is searched
let prelude_def_map = prelude_module.def_map(db); let prelude_def_map = prelude_module.def_map(db);
let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
if let Some((name, vis)) = prelude_scope.name_of(item) { let (name, vis) = prelude_scope.name_of(item)?;
if vis.is_visible_from(db, from) { if !vis.is_visible_from(db, from) {
return Some(Some(ModPath::from_segments(PathKind::Plain, Some(name.clone())))); return None;
} }
}
// Check if the name is in current scope and it points to the same def.
let found_and_same_def =
local_def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
let per_ns = def_map[local_id].scope.get(name);
let same_def = match item {
ItemInNs::Types(it) => per_ns.take_types()? == it,
ItemInNs::Values(it) => per_ns.take_values()? == it,
ItemInNs::Macros(it) => per_ns.take_macros()? == it,
};
Some(same_def)
});
if found_and_same_def.unwrap_or(true) {
Some(ModPath::from_segments(PathKind::Plain, Some(name.clone())))
} else {
None
} }
None
} }
fn find_self_super(def_map: &DefMap, item: ModuleId, from: ModuleId) -> Option<ModPath> { fn find_self_super(def_map: &DefMap, item: ModuleId, from: ModuleId) -> Option<ModPath> {
@ -512,7 +531,7 @@ mod tests {
fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) { fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) {
let (db, pos) = TestDB::with_position(ra_fixture); let (db, pos) = TestDB::with_position(ra_fixture);
let module = db.module_at_position(pos); let module = db.module_at_position(pos);
let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};"));
let ast_path = let ast_path =
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
let mod_path = ModPath::from_src(&db, ast_path, &Hygiene::new_unhygienic()).unwrap(); let mod_path = ModPath::from_src(&db, ast_path, &Hygiene::new_unhygienic()).unwrap();
@ -531,7 +550,7 @@ mod tests {
let found_path = let found_path =
find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind, false); find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind, false);
assert_eq!(found_path, Some(mod_path), "{:?}", prefix_kind); assert_eq!(found_path, Some(mod_path), "{prefix_kind:?}");
} }
fn check_found_path( fn check_found_path(
@ -808,6 +827,48 @@ pub mod prelude {
); );
} }
#[test]
fn shadowed_prelude() {
check_found_path(
r#"
//- /main.rs crate:main deps:std
struct S;
$0
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub struct S;
}
}
"#,
"std::prelude::rust_2018::S",
"std::prelude::rust_2018::S",
"std::prelude::rust_2018::S",
"std::prelude::rust_2018::S",
);
}
#[test]
fn imported_prelude() {
check_found_path(
r#"
//- /main.rs crate:main deps:std
use S;
$0
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub struct S;
}
}
"#,
"S",
"S",
"S",
"S",
);
}
#[test] #[test]
fn enum_variant_from_prelude() { fn enum_variant_from_prelude() {
let code = r#" let code = r#"

View file

@ -142,8 +142,8 @@ pub enum WherePredicateTypeTarget {
impl GenericParams { impl GenericParams {
/// Iterator of type_or_consts field /// Iterator of type_or_consts field
pub fn iter<'a>( pub fn iter(
&'a self, &self,
) -> impl DoubleEndedIterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)> { ) -> impl DoubleEndedIterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)> {
self.type_or_consts.iter() self.type_or_consts.iter()
} }

View file

@ -239,7 +239,7 @@ impl fmt::Debug for ImportMap {
ItemInNs::Values(_) => "v", ItemInNs::Values(_) => "v",
ItemInNs::Macros(_) => "m", ItemInNs::Macros(_) => "m",
}; };
format!("- {} ({})", info.path, ns) format!("- {} ({ns})", info.path)
}) })
.collect(); .collect();
@ -389,12 +389,12 @@ impl Query {
/// Searches dependencies of `krate` for an importable path matching `query`. /// Searches dependencies of `krate` for an importable path matching `query`.
/// ///
/// This returns a list of items that could be imported from dependencies of `krate`. /// This returns a list of items that could be imported from dependencies of `krate`.
pub fn search_dependencies<'a>( pub fn search_dependencies(
db: &'a dyn DefDatabase, db: &dyn DefDatabase,
krate: CrateId, krate: CrateId,
query: Query, query: Query,
) -> FxHashSet<ItemInNs> { ) -> FxHashSet<ItemInNs> {
let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query)); let _p = profile::span("search_dependencies").detail(|| format!("{query:?}"));
let graph = db.crate_graph(); let graph = db.crate_graph();
let import_maps: Vec<_> = let import_maps: Vec<_> =
@ -545,7 +545,7 @@ mod tests {
None None
} }
})?; })?;
return Some(format!("{}::{}", dependency_imports.path_of(trait_)?, assoc_item_name)); return Some(format!("{}::{assoc_item_name}", dependency_imports.path_of(trait_)?));
} }
None None
} }
@ -585,7 +585,7 @@ mod tests {
let map = db.import_map(krate); let map = db.import_map(krate);
Some(format!("{}:\n{:?}\n", name, map)) Some(format!("{name}:\n{map:?}\n"))
}) })
.sorted() .sorted()
.collect::<String>(); .collect::<String>();

View file

@ -96,7 +96,7 @@ pub(crate) enum BuiltinShadowMode {
/// Legacy macros can only be accessed through special methods like `get_legacy_macros`. /// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
/// Other methods will only resolve values, types and module scoped macros only. /// Other methods will only resolve values, types and module scoped macros only.
impl ItemScope { impl ItemScope {
pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { pub fn entries(&self) -> impl Iterator<Item = (&Name, PerNs)> + '_ {
// FIXME: shadowing // FIXME: shadowing
self.types self.types
.keys() .keys()
@ -159,18 +159,17 @@ impl ItemScope {
pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> {
let (def, mut iter) = match item { let (def, mut iter) = match item {
ItemInNs::Macros(def) => { ItemInNs::Macros(def) => {
return self return self.macros.iter().find_map(|(name, &(other_def, vis))| {
.macros (other_def == def).then_some((name, vis))
.iter() });
.find_map(|(name, &(other_def, vis))| (other_def == def).then(|| (name, vis)));
} }
ItemInNs::Types(def) => (def, self.types.iter()), ItemInNs::Types(def) => (def, self.types.iter()),
ItemInNs::Values(def) => (def, self.values.iter()), ItemInNs::Values(def) => (def, self.values.iter()),
}; };
iter.find_map(|(name, &(other_def, vis))| (other_def == def).then(|| (name, vis))) iter.find_map(|(name, &(other_def, vis))| (other_def == def).then_some((name, vis)))
} }
pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { 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 {
@ -327,7 +326,7 @@ impl ItemScope {
changed changed
} }
pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Option<Name>, PerNs)> + 'a { 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 self.unnamed_trait_imports
.iter() .iter()

View file

@ -105,7 +105,7 @@ pub struct ItemTree {
impl ItemTree { impl ItemTree {
pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
let _p = profile::span("file_item_tree_query").detail(|| format!("{:?}", file_id)); let _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}"));
let syntax = match db.parse_or_expand(file_id) { let syntax = match db.parse_or_expand(file_id) {
Some(node) => node, Some(node) => node,
None => return Default::default(), None => return Default::default(),
@ -132,7 +132,7 @@ impl ItemTree {
ctx.lower_macro_stmts(stmts) ctx.lower_macro_stmts(stmts)
}, },
_ => { _ => {
panic!("cannot create item tree from {:?} {}", syntax, syntax); panic!("cannot create item tree from {syntax:?} {syntax}");
}, },
} }
}; };

View file

@ -0,0 +1,96 @@
//! Definitions needed for computing data layout of types.
use std::cmp;
use la_arena::{Idx, RawIdx};
pub use rustc_abi::{
Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
TargetDataLayout, TargetDataLayoutErrors, WrappingRange,
};
use crate::LocalEnumVariantId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
impl rustc_index::vec::Idx for RustcEnumVariantIdx {
fn new(idx: usize) -> Self {
RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
}
fn index(self) -> usize {
u32::from(self.0.into_raw()) as usize
}
}
pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;
pub trait IntegerExt {
fn repr_discr(
dl: &TargetDataLayout,
repr: &ReprOptions,
min: i128,
max: i128,
) -> Result<(Integer, bool), LayoutError>;
}
impl IntegerExt for Integer {
/// Finds the appropriate Integer type and signedness for the given
/// signed discriminant range and `#[repr]` attribute.
/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
/// that shouldn't affect anything, other than maybe debuginfo.
fn repr_discr(
dl: &TargetDataLayout,
repr: &ReprOptions,
min: i128,
max: i128,
) -> Result<(Integer, bool), LayoutError> {
// Theoretically, negative values could be larger in unsigned representation
// than the unsigned representation of the signed minimum. However, if there
// are any negative values, the only valid unsigned representation is u128
// which can fit all i128 values, so the result remains unaffected.
let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
if let Some(ity) = repr.int {
let discr = Integer::from_attr(dl, ity);
let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
if discr < fit {
return Err(LayoutError::UserError(
"Integer::repr_discr: `#[repr]` hint too small for \
discriminant range of enum "
.to_string(),
));
}
return Ok((discr, ity.is_signed()));
}
let at_least = if repr.c() {
// This is usually I32, however it can be different on some platforms,
// notably hexagon and arm-none/thumb-none
dl.c_enum_min_size
} else {
// repr(Rust) enums try to be as small as possible
Integer::I8
};
// If there are no negative values, we can use the unsigned fit.
Ok(if min >= 0 {
(cmp::max(unsigned_fit, at_least), false)
} else {
(cmp::max(signed_fit, at_least), true)
})
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LayoutError {
UserError(String),
SizeOverflow,
HasPlaceholder,
NotImplemented,
Unknown,
}

View file

@ -34,6 +34,7 @@ pub mod adt;
pub mod data; pub mod data;
pub mod generics; pub mod generics;
pub mod lang_item; pub mod lang_item;
pub mod layout;
pub mod expr; pub mod expr;
pub mod body; pub mod body;

View file

@ -170,7 +170,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
} }
let pp = pretty_print_macro_expansion( let pp = pretty_print_macro_expansion(
parse.syntax_node(), parse.syntax_node(),
show_token_ids.then(|| &*token_map), show_token_ids.then_some(&*token_map),
); );
let indent = IndentLevel::from_node(call.syntax()); let indent = IndentLevel::from_node(call.syntax());
let pp = reindent(indent, pp); let pp = reindent(indent, pp);
@ -179,7 +179,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
if tree { if tree {
let tree = format!("{:#?}", parse.syntax_node()) let tree = format!("{:#?}", parse.syntax_node())
.split_inclusive('\n') .split_inclusive('\n')
.map(|line| format!("// {}", line)) .map(|line| format!("// {line}"))
.collect::<String>(); .collect::<String>();
format_to!(expn_text, "\n{}", tree) format_to!(expn_text, "\n{}", tree)
} }

View file

@ -163,7 +163,8 @@ macro_rules! compile_error {
} }
// This expands to nothing (since it's in item position), but emits an error. // This expands to nothing (since it's in item position), but emits an error.
compile_error!("error!"); compile_error!("error, with an escaped quote: \"");
compile_error!(r"this is a raw string");
"#, "#,
expect![[r##" expect![[r##"
#[rustc_builtin_macro] #[rustc_builtin_macro]
@ -172,7 +173,8 @@ macro_rules! compile_error {
($msg:expr,) => ({ /* compiler built-in */ }) ($msg:expr,) => ({ /* compiler built-in */ })
} }
/* error: error! */ /* error: error, with an escaped quote: " */
/* error: this is a raw string */
"##]], "##]],
); );
} }

View file

@ -1630,3 +1630,48 @@ const _: i32 = -0--1--2;
"#]], "#]],
); );
} }
#[test]
fn test_punct_without_space() {
// Puncts are "glued" greedily.
check(
r#"
macro_rules! foo {
(: : :) => { "1 1 1" };
(: ::) => { "1 2" };
(:: :) => { "2 1" };
(: : : :) => { "1 1 1 1" };
(:: : :) => { "2 1 1" };
(: :: :) => { "1 2 1" };
(: : ::) => { "1 1 2" };
(:: ::) => { "2 2" };
}
fn test() {
foo!(:::);
foo!(: :::);
foo!(::::);
}
"#,
expect![[r#"
macro_rules! foo {
(: : :) => { "1 1 1" };
(: ::) => { "1 2" };
(:: :) => { "2 1" };
(: : : :) => { "1 1 1 1" };
(:: : :) => { "2 1 1" };
(: :: :) => { "1 2 1" };
(: : ::) => { "1 1 2" };
(:: ::) => { "2 2" };
}
fn test() {
"2 1";
"1 2 1";
"2 2";
}
"#]],
);
}

View file

@ -136,3 +136,52 @@ macro_rules! m { ($($i:ident)? $vis:vis) => () }
"#]], "#]],
) )
} }
// For this test and the one below, see rust-lang/rust#86730.
#[test]
fn expr_dont_match_let_expr() {
check(
r#"
macro_rules! foo {
($e:expr) => { $e }
}
fn test() {
foo!(let a = 3);
}
"#,
expect![[r#"
macro_rules! foo {
($e:expr) => { $e }
}
fn test() {
/* error: no rule matches input tokens */missing;
}
"#]],
);
}
#[test]
fn expr_dont_match_inline_const() {
check(
r#"
macro_rules! foo {
($e:expr) => { $e }
}
fn test() {
foo!(const { 3 });
}
"#,
expect![[r#"
macro_rules! foo {
($e:expr) => { $e }
}
fn test() {
/* error: no rule matches input tokens */missing;
}
"#]],
);
}

View file

@ -457,7 +457,7 @@ impl DefMap {
for (name, child) in for (name, child) in
map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0)) map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
{ {
let path = format!("{}::{}", path, name); let path = format!("{path}::{name}");
buf.push('\n'); buf.push('\n');
go(buf, map, &path, *child); go(buf, map, &path, *child);
} }

View file

@ -40,7 +40,7 @@ use crate::{
diagnostics::DefDiagnostic, diagnostics::DefDiagnostic,
mod_resolution::ModDir, mod_resolution::ModDir,
path_resolution::ReachedFixedPoint, path_resolution::ReachedFixedPoint,
proc_macro::{ProcMacroDef, ProcMacroKind}, proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode, BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
}, },
path::{ImportAlias, ModPath, PathKind}, path::{ImportAlias, ModPath, PathKind},
@ -67,7 +67,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
let dep_def_map = db.crate_def_map(dep.crate_id); let dep_def_map = db.crate_def_map(dep.crate_id);
let dep_root = dep_def_map.module_id(dep_def_map.root); let dep_root = dep_def_map.module_id(dep_def_map.root);
deps.insert(dep.as_name(), dep_root.into()); deps.insert(dep.as_name(), dep_root);
if dep.is_prelude() && !tree_id.is_block() { if dep.is_prelude() && !tree_id.is_block() {
def_map.extern_prelude.insert(dep.as_name(), dep_root); def_map.extern_prelude.insert(dep.as_name(), dep_root);
@ -1017,7 +1017,7 @@ impl DefCollector<'_> {
None => true, None => true,
Some(old_vis) => { Some(old_vis) => {
let max_vis = old_vis.max(vis, &self.def_map).unwrap_or_else(|| { let max_vis = old_vis.max(vis, &self.def_map).unwrap_or_else(|| {
panic!("`Tr as _` imports with unrelated visibilities {:?} and {:?} (trait {:?})", old_vis, vis, tr); panic!("`Tr as _` imports with unrelated visibilities {old_vis:?} and {vis:?} (trait {tr:?})");
}); });
if max_vis == old_vis { if max_vis == old_vis {
@ -1094,7 +1094,7 @@ impl DefCollector<'_> {
ast_id, ast_id,
*expand_to, *expand_to,
self.def_map.krate, self.def_map.krate,
&resolver_def_id, resolver_def_id,
&mut |_err| (), &mut |_err| (),
); );
if let Ok(Ok(call_id)) = call_id { if let Ok(Ok(call_id)) = call_id {
@ -1110,7 +1110,7 @@ impl DefCollector<'_> {
*derive_attr, *derive_attr,
*derive_pos as u32, *derive_pos as u32,
self.def_map.krate, self.def_map.krate,
&resolver, resolver,
); );
if let Ok((macro_id, def_id, call_id)) = id { if let Ok((macro_id, def_id, call_id)) = id {
@ -1345,7 +1345,7 @@ impl DefCollector<'_> {
// Missing proc macros are non-fatal, so they are handled specially. // Missing proc macros are non-fatal, so they are handled specially.
DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate) DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
} }
_ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()), _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
}; };
self.def_map.diagnostics.push(diag); self.def_map.diagnostics.push(diag);
@ -2005,6 +2005,7 @@ impl ModCollector<'_, '_> {
let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast()); let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast());
// Case 1: builtin macros // Case 1: builtin macros
let mut helpers_opt = None;
let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into()); let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
let expander = if attrs.by_key("rustc_builtin_macro").exists() { let expander = if attrs.by_key("rustc_builtin_macro").exists() {
if let Some(expander) = find_builtin_macro(&mac.name) { if let Some(expander) = find_builtin_macro(&mac.name) {
@ -2013,6 +2014,25 @@ impl ModCollector<'_, '_> {
Either::Right(it) => MacroExpander::BuiltInEager(it), Either::Right(it) => MacroExpander::BuiltInEager(it),
} }
} else if let Some(expander) = find_builtin_derive(&mac.name) { } else if let Some(expander) = find_builtin_derive(&mac.name) {
if let Some(attr) = attrs.by_key("rustc_builtin_macro").tt_values().next() {
// NOTE: The item *may* have both `#[rustc_builtin_macro]` and `#[proc_macro_derive]`,
// in which case rustc ignores the helper attributes from the latter, but it
// "doesn't make sense in practice" (see rust-lang/rust#87027).
if let Some((name, helpers)) =
parse_macro_name_and_helper_attrs(&attr.token_trees)
{
// NOTE: rustc overrides the name if the macro name if it's different from the
// macro name, but we assume it isn't as there's no such case yet. FIXME if
// the following assertion fails.
stdx::always!(
name == mac.name,
"built-in macro {} has #[rustc_builtin_macro] which declares different name {}",
mac.name,
name
);
helpers_opt = Some(helpers);
}
}
MacroExpander::BuiltInDerive(expander) MacroExpander::BuiltInDerive(expander)
} else if let Some(expander) = find_builtin_attr(&mac.name) { } else if let Some(expander) = find_builtin_attr(&mac.name) {
MacroExpander::BuiltInAttr(expander) MacroExpander::BuiltInAttr(expander)
@ -2037,6 +2057,12 @@ impl ModCollector<'_, '_> {
macro_id, macro_id,
&self.item_tree[mac.visibility], &self.item_tree[mac.visibility],
); );
if let Some(helpers) = helpers_opt {
self.def_collector
.def_map
.exported_derives
.insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
}
} }
fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) { fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) {
@ -2059,7 +2085,7 @@ impl ModCollector<'_, '_> {
.scope .scope
.get_legacy_macro(name) .get_legacy_macro(name)
.and_then(|it| it.last()) .and_then(|it| it.last())
.map(|&it| macro_id_to_def_id(self.def_collector.db, it.into())) .map(|&it| macro_id_to_def_id(self.def_collector.db, it))
}, },
) )
}) })

View file

@ -34,7 +34,7 @@ impl ModDir {
let path = match attr_path.map(SmolStr::as_str) { let path = match attr_path.map(SmolStr::as_str) {
None => { None => {
let mut path = self.dir_path.clone(); let mut path = self.dir_path.clone();
path.push(&name.to_smol_str()); path.push(&name.unescaped().to_smol_str());
path path
} }
Some(attr_path) => { Some(attr_path) => {
@ -74,12 +74,12 @@ impl ModDir {
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
} }
None if file_id.is_include_macro(db.upcast()) => { None if file_id.is_include_macro(db.upcast()) => {
candidate_files.push(format!("{}.rs", name)); candidate_files.push(format!("{name}.rs"));
candidate_files.push(format!("{}/mod.rs", name)); candidate_files.push(format!("{name}/mod.rs"));
} }
None => { None => {
candidate_files.push(format!("{}{}.rs", self.dir_path.0, name)); candidate_files.push(format!("{}{name}.rs", self.dir_path.0));
candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name)); candidate_files.push(format!("{}{name}/mod.rs", self.dir_path.0));
} }
}; };
@ -91,7 +91,7 @@ impl ModDir {
let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() { let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
(DirPath::empty(), false) (DirPath::empty(), false)
} else { } else {
(DirPath::new(format!("{}/", name)), true) (DirPath::new(format!("{name}/")), true)
}; };
if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) { if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
return Ok((file_id, is_mod_rs, mod_dir)); return Ok((file_id, is_mod_rs, mod_dir));
@ -156,7 +156,7 @@ impl DirPath {
} else { } else {
attr attr
}; };
let res = format!("{}{}", base, attr); let res = format!("{base}{attr}");
res res
} }
} }

View file

@ -170,8 +170,8 @@ impl DefMap {
) -> ResolvePathResult { ) -> ResolvePathResult {
let graph = db.crate_graph(); let graph = db.crate_graph();
let _cx = stdx::panic_context::enter(format!( let _cx = stdx::panic_context::enter(format!(
"DefMap {:?} crate_name={:?} block={:?} path={}", "DefMap {:?} crate_name={:?} block={:?} path={path}",
self.krate, graph[self.krate].display_name, self.block, path self.krate, graph[self.krate].display_name, self.block
)); ));
let mut segments = path.segments().iter().enumerate(); let mut segments = path.segments().iter().enumerate();
@ -390,7 +390,7 @@ impl DefMap {
.get_legacy_macro(name) .get_legacy_macro(name)
// FIXME: shadowing // FIXME: shadowing
.and_then(|it| it.last()) .and_then(|it| it.last())
.map_or_else(PerNs::none, |&m| PerNs::macros(m.into(), Visibility::Public)); .map_or_else(PerNs::none, |&m| PerNs::macros(m, Visibility::Public));
let from_scope = self[module].scope.get(name); let from_scope = self[module].scope.get(name);
let from_builtin = match self.block { let from_builtin = match self.block {
Some(_) => { Some(_) => {

View file

@ -37,45 +37,53 @@ impl Attrs {
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr }) Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
} else if self.by_key("proc_macro_derive").exists() { } else if self.by_key("proc_macro_derive").exists() {
let derive = self.by_key("proc_macro_derive").tt_values().next()?; let derive = self.by_key("proc_macro_derive").tt_values().next()?;
let def = parse_macro_name_and_helper_attrs(&derive.token_trees)
.map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::CustomDerive { helpers } });
match &*derive.token_trees { if def.is_none() {
// `#[proc_macro_derive(Trait)]` tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);
[TokenTree::Leaf(Leaf::Ident(trait_name))] => Some(ProcMacroDef {
name: trait_name.as_name(),
kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) },
}),
// `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]`
[
TokenTree::Leaf(Leaf::Ident(trait_name)),
TokenTree::Leaf(Leaf::Punct(comma)),
TokenTree::Leaf(Leaf::Ident(attributes)),
TokenTree::Subtree(helpers)
] if comma.char == ',' && attributes.text == "attributes" =>
{
let helpers = helpers.token_trees.iter()
.filter(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','))
.map(|tt| {
match tt {
TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
_ => None
}
})
.collect::<Option<Box<[_]>>>()?;
Some(ProcMacroDef {
name: trait_name.as_name(),
kind: ProcMacroKind::CustomDerive { helpers },
})
}
_ => {
tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);
None
}
} }
def
} else { } else {
None None
} }
} }
} }
// This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
// the same strucuture.
#[rustfmt::skip]
pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> {
match tt {
// `#[proc_macro_derive(Trait)]`
// `#[rustc_builtin_macro(Trait)]`
[TokenTree::Leaf(Leaf::Ident(trait_name))] => Some((trait_name.as_name(), Box::new([]))),
// `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]`
// `#[rustc_builtin_macro(Trait, attributes(helper1, helper2, ...))]`
[
TokenTree::Leaf(Leaf::Ident(trait_name)),
TokenTree::Leaf(Leaf::Punct(comma)),
TokenTree::Leaf(Leaf::Ident(attributes)),
TokenTree::Subtree(helpers)
] if comma.char == ',' && attributes.text == "attributes" =>
{
let helpers = helpers
.token_trees
.iter()
.filter(
|tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','),
)
.map(|tt| match tt {
TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
_ => None,
})
.collect::<Option<Box<[_]>>>()?;
Some((trait_name.as_name(), helpers))
}
_ => None,
}
}

View file

@ -13,7 +13,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
let events = db.log_executed(|| { let events = db.log_executed(|| {
db.crate_def_map(krate); db.crate_def_map(krate);
}); });
assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
} }
db.set_file_text(pos.file_id, Arc::new(ra_fixture_change.to_string())); db.set_file_text(pos.file_id, Arc::new(ra_fixture_change.to_string()));
@ -21,7 +21,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
let events = db.log_executed(|| { let events = db.log_executed(|| {
db.crate_def_map(krate); db.crate_def_map(krate);
}); });
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) assert!(!format!("{events:?}").contains("crate_def_map"), "{events:#?}")
} }
} }
@ -94,7 +94,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
assert_eq!(module_data.scope.resolutions().count(), 1); assert_eq!(module_data.scope.resolutions().count(), 1);
}); });
assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
} }
db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string())); db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string()));
@ -104,7 +104,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
assert_eq!(module_data.scope.resolutions().count(), 1); assert_eq!(module_data.scope.resolutions().count(), 1);
}); });
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) assert!(!format!("{events:?}").contains("crate_def_map"), "{events:#?}")
} }
} }

View file

@ -822,6 +822,28 @@ fn derive() {}
); );
} }
#[test]
fn resolves_derive_helper_rustc_builtin_macro() {
cov_mark::check!(resolved_derive_helper);
// This is NOT the correct usage of `default` helper attribute, but we don't resolve helper
// attributes on non mod items in hir nameres.
check(
r#"
//- minicore: derive, default
#[derive(Default)]
#[default]
enum E {
A,
B,
}
"#,
expect![[r#"
crate
E: t
"#]],
);
}
#[test] #[test]
fn unresolved_attr_with_cfg_attr_hang() { fn unresolved_attr_with_cfg_attr_hang() {
// Another regression test for https://github.com/rust-lang/rust-analyzer/issues/8905 // Another regression test for https://github.com/rust-lang/rust-analyzer/issues/8905

View file

@ -156,6 +156,43 @@ pub struct Baz;
); );
} }
#[test]
fn module_resolution_works_for_inline_raw_modules() {
check(
r#"
//- /lib.rs
mod r#async {
pub mod a;
pub mod r#async;
}
use self::r#async::a::Foo;
use self::r#async::r#async::Bar;
//- /async/a.rs
pub struct Foo;
//- /async/async.rs
pub struct Bar;
"#,
expect![[r#"
crate
Bar: t v
Foo: t v
r#async: t
crate::r#async
a: t
r#async: t
crate::r#async::a
Foo: t v
crate::r#async::r#async
Bar: t v
"#]],
);
}
#[test] #[test]
fn module_resolution_decl_path() { fn module_resolution_decl_path() {
check( check(

View file

@ -92,7 +92,7 @@ pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) ->
pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result { pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result {
match arg { match arg {
GenericArg::Type(ty) => print_type_ref(ty, buf), GenericArg::Type(ty) => print_type_ref(ty, buf),
GenericArg::Const(c) => write!(buf, "{}", c), GenericArg::Const(c) => write!(buf, "{c}"),
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name), GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name),
} }
} }
@ -118,7 +118,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
Mutability::Shared => "*const", Mutability::Shared => "*const",
Mutability::Mut => "*mut", Mutability::Mut => "*mut",
}; };
write!(buf, "{} ", mtbl)?; write!(buf, "{mtbl} ")?;
print_type_ref(pointee, buf)?; print_type_ref(pointee, buf)?;
} }
TypeRef::Reference(pointee, lt, mtbl) => { TypeRef::Reference(pointee, lt, mtbl) => {
@ -130,13 +130,13 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
if let Some(lt) = lt { if let Some(lt) = lt {
write!(buf, "{} ", lt.name)?; write!(buf, "{} ", lt.name)?;
} }
write!(buf, "{}", mtbl)?; write!(buf, "{mtbl}")?;
print_type_ref(pointee, buf)?; print_type_ref(pointee, buf)?;
} }
TypeRef::Array(elem, len) => { TypeRef::Array(elem, len) => {
write!(buf, "[")?; write!(buf, "[")?;
print_type_ref(elem, buf)?; print_type_ref(elem, buf)?;
write!(buf, "; {}]", len)?; write!(buf, "; {len}]")?;
} }
TypeRef::Slice(elem) => { TypeRef::Slice(elem) => {
write!(buf, "[")?; write!(buf, "[")?;

View file

@ -381,7 +381,7 @@ impl Resolver {
}); });
def_map[module_id].scope.legacy_macros().for_each(|(name, macs)| { def_map[module_id].scope.legacy_macros().for_each(|(name, macs)| {
macs.iter().for_each(|&mac| { macs.iter().for_each(|&mac| {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(MacroId::from(mac)))); res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
}) })
}); });
def_map.extern_prelude().for_each(|(name, &def)| { def_map.extern_prelude().for_each(|(name, &def)| {
@ -517,10 +517,7 @@ impl Scope {
}); });
m.def_map[m.module_id].scope.legacy_macros().for_each(|(name, macs)| { m.def_map[m.module_id].scope.legacy_macros().for_each(|(name, macs)| {
macs.iter().for_each(|&mac| { macs.iter().for_each(|&mac| {
acc.add( acc.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
name,
ScopeDef::ModuleDef(ModuleDefId::MacroId(MacroId::from(mac))),
);
}) })
}); });
} }

View file

@ -115,7 +115,8 @@ pub fn pseudo_derive_attr_expansion(
}; };
let mut token_trees = Vec::new(); let mut token_trees = Vec::new();
for tt in (&args.token_trees) for tt in args
.token_trees
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. })))) .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))))
{ {
token_trees.push(mk_leaf('#')); token_trees.push(mk_leaf('#'));

View file

@ -379,15 +379,10 @@ fn compile_error_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<ExpandedEager> { ) -> ExpandResult<ExpandedEager> {
let err = match &*tt.token_trees { let err = match &*tt.token_trees {
[tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => { [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
let text = it.text.as_str(); Some(unquoted) => ExpandError::Other(unquoted.into()),
if text.starts_with('"') && text.ends_with('"') { None => ExpandError::Other("`compile_error!` argument must be a string".into()),
// FIXME: does not handle raw strings },
ExpandError::Other(text[1..text.len() - 1].into())
} else {
ExpandError::Other("`compile_error!` argument must be a string".into())
}
}
_ => ExpandError::Other("`compile_error!` argument must be a string".into()), _ => ExpandError::Other("`compile_error!` argument must be a string".into()),
}; };
@ -454,7 +449,7 @@ fn concat_bytes_expand(
match token.kind() { match token.kind() {
syntax::SyntaxKind::BYTE => bytes.push(token.text().to_string()), syntax::SyntaxKind::BYTE => bytes.push(token.text().to_string()),
syntax::SyntaxKind::BYTE_STRING => { syntax::SyntaxKind::BYTE_STRING => {
let components = unquote_byte_string(lit).unwrap_or_else(Vec::new); let components = unquote_byte_string(lit).unwrap_or_default();
components.into_iter().for_each(|x| bytes.push(x.to_string())); components.into_iter().for_each(|x| bytes.push(x.to_string()));
} }
_ => { _ => {
@ -676,7 +671,7 @@ fn option_env_expand(
let expanded = match get_env_inner(db, arg_id, &key) { let expanded = match get_env_inner(db, arg_id, &key) {
None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> }, None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> },
Some(s) => quote! { #DOLLAR_CRATE::option::Some(#s) }, Some(s) => quote! { #DOLLAR_CRATE::option::Option::Some(#s) },
}; };
ExpandResult::ok(ExpandedEager::new(expanded)) ExpandResult::ok(ExpandedEager::new(expanded))

View file

@ -240,7 +240,7 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
} }
fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
match file_id.0 { match file_id.repr() {
HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
HirFileIdRepr::MacroFile(macro_file) => { HirFileIdRepr::MacroFile(macro_file) => {
// FIXME: Note how we convert from `Parse` to `SyntaxNode` here, // FIXME: Note how we convert from `Parse` to `SyntaxNode` here,
@ -444,7 +444,7 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
// be reported at the definition site (when we construct a def map). // be reported at the definition site (when we construct a def map).
Err(err) => { Err(err) => {
return ExpandResult::only_err(ExpandError::Other( return ExpandResult::only_err(ExpandError::Other(
format!("invalid macro definition: {}", err).into(), format!("invalid macro definition: {err}").into(),
)) ))
} }
}; };

View file

@ -161,7 +161,7 @@ pub fn expand_eager_macro(
Ok(Ok(db.intern_macro_call(loc))) Ok(Ok(db.intern_macro_call(loc)))
} else { } else {
panic!("called `expand_eager_macro` on non-eager macro def {:?}", def); panic!("called `expand_eager_macro` on non-eager macro def {def:?}");
} }
} }
@ -208,7 +208,7 @@ fn eager_macro_recur(
// Collect replacement // Collect replacement
for child in children { for child in children {
let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) { let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
Some(path) => macro_resolver(path.clone()).ok_or_else(|| UnresolvedMacro { path })?, Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
None => { None => {
diagnostic_sink(ExpandError::Other("malformed macro invocation".into())); diagnostic_sink(ExpandError::Other("malformed macro invocation".into()));
continue; continue;

View file

@ -366,7 +366,7 @@ mod tests {
fixups.append, fixups.append,
); );
let actual = format!("{}\n", tt); let actual = format!("{tt}\n");
expect.indent(false); expect.indent(false);
expect.assert_eq(&actual); expect.assert_eq(&actual);

View file

@ -17,7 +17,7 @@ use crate::{
db::{self, AstDatabase}, db::{self, AstDatabase},
fixup, fixup,
name::{AsName, Name}, name::{AsName, Name},
HirFileId, HirFileIdRepr, InFile, MacroCallKind, MacroCallLoc, MacroDefKind, MacroFile, HirFileId, InFile, MacroCallKind, MacroCallLoc, MacroDefKind, MacroFile,
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -216,9 +216,9 @@ fn make_hygiene_info(
impl HygieneFrame { impl HygieneFrame {
pub(crate) fn new(db: &dyn AstDatabase, file_id: HirFileId) -> HygieneFrame { pub(crate) fn new(db: &dyn AstDatabase, file_id: HirFileId) -> HygieneFrame {
let (info, krate, local_inner) = match file_id.0 { let (info, krate, local_inner) = match file_id.macro_file() {
HirFileIdRepr::FileId(_) => (None, None, false), None => (None, None, false),
HirFileIdRepr::MacroFile(macro_file) => { Some(macro_file) => {
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id); let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let info = let info =
make_hygiene_info(db, macro_file, &loc).map(|info| (loc.kind.file_id(), info)); make_hygiene_info(db, macro_file, &loc).map(|info| (loc.kind.file_id(), info));

View file

@ -23,7 +23,11 @@ pub use mbe::{Origin, ValueResult};
use std::{fmt, hash::Hash, iter, sync::Arc}; use std::{fmt, hash::Hash, iter, sync::Arc};
use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange, ProcMacroKind}; use base_db::{
impl_intern_key,
salsa::{self, InternId},
CrateId, FileId, FileRange, ProcMacroKind,
};
use either::Either; use either::Either;
use syntax::{ use syntax::{
algo::{self, skip_trivia_token}, algo::{self, skip_trivia_token},
@ -79,26 +83,12 @@ impl fmt::Display for ExpandError {
/// finite (because everything bottoms out at the real `FileId`) and small /// finite (because everything bottoms out at the real `FileId`) and small
/// (`MacroCallId` uses the location interning. You can check details here: /// (`MacroCallId` uses the location interning. You can check details here:
/// <https://en.wikipedia.org/wiki/String_interning>). /// <https://en.wikipedia.org/wiki/String_interning>).
///
/// The two variants are encoded in a single u32 which are differentiated by the MSB.
/// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a
/// `MacroCallId`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HirFileId(HirFileIdRepr); pub struct HirFileId(u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum HirFileIdRepr {
FileId(FileId),
MacroFile(MacroFile),
}
impl From<FileId> for HirFileId {
fn from(id: FileId) -> Self {
HirFileId(HirFileIdRepr::FileId(id))
}
}
impl From<MacroFile> for HirFileId {
fn from(id: MacroFile) -> Self {
HirFileId(HirFileIdRepr::MacroFile(id))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroFile { pub struct MacroFile {
@ -172,13 +162,37 @@ pub enum MacroCallKind {
}, },
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum HirFileIdRepr {
FileId(FileId),
MacroFile(MacroFile),
}
impl From<FileId> for HirFileId {
fn from(FileId(id): FileId) -> Self {
assert!(id < Self::MAX_FILE_ID);
HirFileId(id)
}
}
impl From<MacroFile> for HirFileId {
fn from(MacroFile { macro_call_id: MacroCallId(id) }: MacroFile) -> Self {
let id = id.as_u32();
assert!(id < Self::MAX_FILE_ID);
HirFileId(id | Self::MACRO_FILE_TAG_MASK)
}
}
impl HirFileId { impl HirFileId {
const MAX_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
const MACRO_FILE_TAG_MASK: u32 = 1 << 31;
/// For macro-expansion files, returns the file original source file the /// For macro-expansion files, returns the file original source file the
/// expansion originated from. /// expansion originated from.
pub fn original_file(self, db: &dyn db::AstDatabase) -> FileId { pub fn original_file(self, db: &dyn db::AstDatabase) -> FileId {
let mut file_id = self; let mut file_id = self;
loop { loop {
match file_id.0 { match file_id.repr() {
HirFileIdRepr::FileId(id) => break id, HirFileIdRepr::FileId(id) => break id,
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => { HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
@ -194,7 +208,7 @@ impl HirFileId {
pub fn expansion_level(self, db: &dyn db::AstDatabase) -> u32 { pub fn expansion_level(self, db: &dyn db::AstDatabase) -> u32 {
let mut level = 0; let mut level = 0;
let mut curr = self; let mut curr = self;
while let HirFileIdRepr::MacroFile(macro_file) = curr.0 { while let Some(macro_file) = curr.macro_file() {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
level += 1; level += 1;
@ -205,25 +219,17 @@ impl HirFileId {
/// If this is a macro call, returns the syntax node of the call. /// If this is a macro call, returns the syntax node of the call.
pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> { pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
match self.0 { let macro_file = self.macro_file()?;
HirFileIdRepr::FileId(_) => None, let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
HirFileIdRepr::MacroFile(macro_file) => { Some(loc.kind.to_node(db))
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
Some(loc.kind.to_node(db))
}
}
} }
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in. /// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
pub fn original_call_node(self, db: &dyn db::AstDatabase) -> Option<(FileId, SyntaxNode)> { pub fn original_call_node(self, db: &dyn db::AstDatabase) -> Option<(FileId, SyntaxNode)> {
let mut call = match self.0 { let mut call =
HirFileIdRepr::FileId(_) => return None, db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).kind.to_node(db);
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
db.lookup_intern_macro_call(macro_call_id).kind.to_node(db)
}
};
loop { loop {
match call.file_id.0 { match call.file_id.repr() {
HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)), HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)),
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => { HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
call = db.lookup_intern_macro_call(macro_call_id).kind.to_node(db); call = db.lookup_intern_macro_call(macro_call_id).kind.to_node(db);
@ -234,84 +240,74 @@ impl HirFileId {
/// Return expansion information if it is a macro-expansion file /// Return expansion information if it is a macro-expansion file
pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> { pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
match self.0 { let macro_file = self.macro_file()?;
HirFileIdRepr::FileId(_) => None, let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
HirFileIdRepr::MacroFile(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let arg_tt = loc.kind.arg(db)?; let arg_tt = loc.kind.arg(db)?;
let macro_def = db.macro_def(loc.def).ok()?; let macro_def = db.macro_def(loc.def).ok()?;
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?; let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
let macro_arg = db.macro_arg(macro_file.macro_call_id)?; let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
let def = loc.def.ast_id().left().and_then(|id| { let def = loc.def.ast_id().left().and_then(|id| {
let def_tt = match id.to_node(db) { let def_tt = match id.to_node(db) {
ast::Macro::MacroRules(mac) => mac.token_tree()?, ast::Macro::MacroRules(mac) => mac.token_tree()?,
ast::Macro::MacroDef(_) ast::Macro::MacroDef(_) if matches!(*macro_def, TokenExpander::BuiltinAttr(_)) => {
if matches!(*macro_def, TokenExpander::BuiltinAttr(_)) => return None
{ }
return None ast::Macro::MacroDef(mac) => mac.body()?,
} };
ast::Macro::MacroDef(mac) => mac.body()?, Some(InFile::new(id.file_id, def_tt))
}; });
Some(InFile::new(id.file_id, def_tt)) let attr_input_or_mac_def = def.or_else(|| match loc.kind {
}); MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
let attr_input_or_mac_def = def.or_else(|| match loc.kind { let tt = ast_id
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { .to_node(db)
let tt = ast_id .doc_comments_and_attrs()
.to_node(db) .nth(invoc_attr_index as usize)
.doc_comments_and_attrs() .and_then(Either::left)?
.nth(invoc_attr_index as usize) .token_tree()?;
.and_then(Either::left)? Some(InFile::new(ast_id.file_id, tt))
.token_tree()?;
Some(InFile::new(ast_id.file_id, tt))
}
_ => None,
});
Some(ExpansionInfo {
expanded: InFile::new(self, parse.syntax_node()),
arg: InFile::new(loc.kind.file_id(), arg_tt),
attr_input_or_mac_def,
macro_arg_shift: mbe::Shift::new(&macro_arg.0),
macro_arg,
macro_def,
exp_map,
})
} }
} _ => None,
});
Some(ExpansionInfo {
expanded: InFile::new(self, parse.syntax_node()),
arg: InFile::new(loc.kind.file_id(), arg_tt),
attr_input_or_mac_def,
macro_arg_shift: mbe::Shift::new(&macro_arg.0),
macro_arg,
macro_def,
exp_map,
})
} }
/// Indicate it is macro file generated for builtin derive /// Indicate it is macro file generated for builtin derive
pub fn is_builtin_derive(&self, db: &dyn db::AstDatabase) -> Option<InFile<ast::Attr>> { pub fn is_builtin_derive(&self, db: &dyn db::AstDatabase) -> Option<InFile<ast::Attr>> {
match self.0 { let macro_file = self.macro_file()?;
HirFileIdRepr::FileId(_) => None, let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
HirFileIdRepr::MacroFile(macro_file) => { let attr = match loc.def.kind {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db),
let attr = match loc.def.kind { _ => return None,
MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db), };
_ => return None, Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
};
Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
}
}
} }
pub fn is_custom_derive(&self, db: &dyn db::AstDatabase) -> bool { pub fn is_custom_derive(&self, db: &dyn db::AstDatabase) -> bool {
match self.0 { match self.macro_file() {
HirFileIdRepr::FileId(_) => false, Some(macro_file) => {
HirFileIdRepr::MacroFile(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
matches!(loc.def.kind, MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _)) matches!(loc.def.kind, MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _))
} }
None => false,
} }
} }
/// Return whether this file is an include macro /// Return whether this file is an include macro
pub fn is_include_macro(&self, db: &dyn db::AstDatabase) -> bool { pub fn is_include_macro(&self, db: &dyn db::AstDatabase) -> bool {
match self.0 { match self.macro_file() {
HirFileIdRepr::MacroFile(macro_file) => { Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
matches!(loc.eager, Some(EagerCallInfo { included_file: Some(_), .. })) matches!(loc.eager, Some(EagerCallInfo { included_file: Some(_), .. }))
} }
@ -321,8 +317,8 @@ impl HirFileId {
/// Return whether this file is an attr macro /// Return whether this file is an attr macro
pub fn is_attr_macro(&self, db: &dyn db::AstDatabase) -> bool { pub fn is_attr_macro(&self, db: &dyn db::AstDatabase) -> bool {
match self.0 { match self.macro_file() {
HirFileIdRepr::MacroFile(macro_file) => { Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
matches!(loc.kind, MacroCallKind::Attr { .. }) matches!(loc.kind, MacroCallKind::Attr { .. })
} }
@ -333,23 +329,36 @@ impl HirFileId {
/// Return whether this file is the pseudo expansion of the derive attribute. /// Return whether this file is the pseudo expansion of the derive attribute.
/// See [`crate::builtin_attr_macro::derive_attr_expand`]. /// See [`crate::builtin_attr_macro::derive_attr_expand`].
pub fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::AstDatabase) -> bool { pub fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::AstDatabase) -> bool {
match self.0 { match self.macro_file() {
HirFileIdRepr::MacroFile(macro_file) => { Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
matches!(loc.kind, MacroCallKind::Attr { is_derive: true, .. }) matches!(loc.kind, MacroCallKind::Attr { is_derive: true, .. })
} }
_ => false, None => false,
} }
} }
#[inline]
pub fn is_macro(self) -> bool { pub fn is_macro(self) -> bool {
matches!(self.0, HirFileIdRepr::MacroFile(_)) self.0 & Self::MACRO_FILE_TAG_MASK != 0
} }
#[inline]
pub fn macro_file(self) -> Option<MacroFile> { pub fn macro_file(self) -> Option<MacroFile> {
match self.0 { match self.0 & Self::MACRO_FILE_TAG_MASK {
HirFileIdRepr::FileId(_) => None, 0 => None,
HirFileIdRepr::MacroFile(m) => Some(m), _ => Some(MacroFile {
macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
}),
}
}
fn repr(self) -> HirFileIdRepr {
match self.0 & Self::MACRO_FILE_TAG_MASK {
0 => HirFileIdRepr::FileId(FileId(self.0)),
_ => HirFileIdRepr::MacroFile(MacroFile {
macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
}),
} }
} }
} }
@ -442,7 +451,7 @@ impl MacroCallKind {
pub fn original_call_range_with_body(self, db: &dyn db::AstDatabase) -> FileRange { pub fn original_call_range_with_body(self, db: &dyn db::AstDatabase) -> FileRange {
let mut kind = self; let mut kind = self;
let file_id = loop { let file_id = loop {
match kind.file_id().0 { match kind.file_id().repr() {
HirFileIdRepr::MacroFile(file) => { HirFileIdRepr::MacroFile(file) => {
kind = db.lookup_intern_macro_call(file.macro_call_id).kind; kind = db.lookup_intern_macro_call(file.macro_call_id).kind;
} }
@ -467,7 +476,7 @@ impl MacroCallKind {
pub fn original_call_range(self, db: &dyn db::AstDatabase) -> FileRange { pub fn original_call_range(self, db: &dyn db::AstDatabase) -> FileRange {
let mut kind = self; let mut kind = self;
let file_id = loop { let file_id = loop {
match kind.file_id().0 { match kind.file_id().repr() {
HirFileIdRepr::MacroFile(file) => { HirFileIdRepr::MacroFile(file) => {
kind = db.lookup_intern_macro_call(file.macro_call_id).kind; kind = db.lookup_intern_macro_call(file.macro_call_id).kind;
} }
@ -779,7 +788,7 @@ impl<'a> InFile<&'a SyntaxNode> {
/// For attributes and derives, this will point back to the attribute only. /// For attributes and derives, this will point back to the attribute only.
/// For the entire item `InFile::use original_file_range_full`. /// For the entire item `InFile::use original_file_range_full`.
pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange { pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
match self.file_id.0 { match self.file_id.repr() {
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
HirFileIdRepr::MacroFile(mac_file) => { HirFileIdRepr::MacroFile(mac_file) => {
if let Some(res) = self.original_file_range_opt(db) { if let Some(res) = self.original_file_range_opt(db) {
@ -846,7 +855,7 @@ impl InFile<SyntaxToken> {
/// Falls back to the macro call range if the node cannot be mapped up fully. /// Falls back to the macro call range if the node cannot be mapped up fully.
pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange { pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
match self.file_id.0 { match self.file_id.repr() {
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
HirFileIdRepr::MacroFile(mac_file) => { HirFileIdRepr::MacroFile(mac_file) => {
if let Some(res) = self.original_file_range_opt(db) { if let Some(res) = self.original_file_range_opt(db) {
@ -861,7 +870,7 @@ impl InFile<SyntaxToken> {
/// Attempts to map the syntax node back up its macro calls. /// Attempts to map the syntax node back up its macro calls.
pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option<FileRange> { pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option<FileRange> {
match self.file_id.0 { match self.file_id.repr() {
HirFileIdRepr::FileId(file_id) => { HirFileIdRepr::FileId(file_id) => {
Some(FileRange { file_id, range: self.value.text_range() }) Some(FileRange { file_id, range: self.value.text_range() })
} }

View file

@ -62,7 +62,7 @@ impl<'a> UnescapedName<'a> {
it.clone() it.clone()
} }
} }
Repr::TupleField(it) => SmolStr::new(&it.to_string()), Repr::TupleField(it) => SmolStr::new(it.to_string()),
} }
} }
} }
@ -139,7 +139,7 @@ impl Name {
pub fn to_smol_str(&self) -> SmolStr { pub fn to_smol_str(&self) -> SmolStr {
match &self.0 { match &self.0 {
Repr::Text(it) => it.clone(), Repr::Text(it) => it.clone(),
Repr::TupleField(it) => SmolStr::new(&it.to_string()), Repr::TupleField(it) => SmolStr::new(it.to_string()),
} }
} }
@ -338,44 +338,6 @@ pub mod known {
test_case, test_case,
recursion_limit, recursion_limit,
feature, feature,
// Safe intrinsics
abort,
add_with_overflow,
black_box,
bitreverse,
bswap,
caller_location,
ctlz,
ctpop,
cttz,
discriminant_value,
forget,
likely,
maxnumf32,
maxnumf64,
min_align_of_val,
min_align_of,
minnumf32,
minnumf64,
mul_with_overflow,
needs_drop,
ptr_guaranteed_eq,
ptr_guaranteed_ne,
rotate_left,
rotate_right,
rustc_peek,
saturating_add,
saturating_sub,
size_of_val,
size_of,
sub_with_overflow,
type_id,
type_name,
unlikely,
variant_count,
wrapping_add,
wrapping_mul,
wrapping_sub,
// known methods of lang items // known methods of lang items
eq, eq,
ne, ne,
@ -419,6 +381,8 @@ pub mod known {
shr, shr,
sub_assign, sub_assign,
sub, sub,
unsafe_cell,
va_list
); );
// self/Self cannot be used as an identifier // self/Self cannot be used as an identifier

View file

@ -233,7 +233,7 @@ mod tests {
let quoted = quote!(#a); let quoted = quote!(#a);
assert_eq!(quoted.to_string(), "hello"); assert_eq!(quoted.to_string(), "hello");
let t = format!("{:?}", quoted); let t = format!("{quoted:?}");
assert_eq!(t, "SUBTREE $\n IDENT hello 4294967295"); assert_eq!(t, "SUBTREE $\n IDENT hello 4294967295");
} }

View file

@ -13,18 +13,20 @@ doctest = false
cov-mark = "2.0.0-pre.1" cov-mark = "2.0.0-pre.1"
itertools = "0.10.5" itertools = "0.10.5"
arrayvec = "0.7.2" arrayvec = "0.7.2"
bitflags = "1.3.2"
smallvec = "1.10.0" smallvec = "1.10.0"
ena = "0.14.0" ena = "0.14.0"
tracing = "0.1.35" tracing = "0.1.35"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
scoped-tls = "1.0.0" scoped-tls = "1.0.0"
chalk-solve = { version = "0.86.0", default-features = false } chalk-solve = { version = "0.88.0", default-features = false }
chalk-ir = "0.86.0" chalk-ir = "0.88.0"
chalk-recursive = { version = "0.86.0", default-features = false } chalk-recursive = { version = "0.88.0", default-features = false }
chalk-derive = "0.86.0" chalk-derive = "0.88.0"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" } la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
once_cell = "1.15.0" once_cell = "1.15.0"
typed-arena = "2.0.1" typed-arena = "2.0.1"
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
stdx = { path = "../stdx", version = "0.0.0" } stdx = { path = "../stdx", version = "0.0.0" }
hir-def = { path = "../hir-def", version = "0.0.0" } hir-def = { path = "../hir-def", version = "0.0.0" }

View file

@ -82,11 +82,11 @@ pub(crate) fn autoderef_step(
} }
// FIXME: replace uses of this with Autoderef above // FIXME: replace uses of this with Autoderef above
pub fn autoderef<'a>( pub fn autoderef(
db: &'a dyn HirDatabase, db: &dyn HirDatabase,
env: Arc<TraitEnvironment>, env: Arc<TraitEnvironment>,
ty: Canonical<Ty>, ty: Canonical<Ty>,
) -> impl Iterator<Item = Canonical<Ty>> + 'a { ) -> impl Iterator<Item = Canonical<Ty>> + '_ {
let mut table = InferenceTable::new(db, env); let mut table = InferenceTable::new(db, env);
let ty = table.instantiate_canonical(ty); let ty = table.instantiate_canonical(ty);
let mut autoderef = Autoderef::new(&mut table, ty); let mut autoderef = Autoderef::new(&mut table, ty);

View file

@ -142,7 +142,7 @@ impl<D> TyBuilder<D> {
match (a.data(Interner), e) { match (a.data(Interner), e) {
(chalk_ir::GenericArgData::Ty(_), ParamKind::Type) (chalk_ir::GenericArgData::Ty(_), ParamKind::Type)
| (chalk_ir::GenericArgData::Const(_), ParamKind::Const(_)) => (), | (chalk_ir::GenericArgData::Const(_), ParamKind::Const(_)) => (),
_ => panic!("Mismatched kinds: {:?}, {:?}, {:?}", a, self.vec, self.param_kinds), _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds),
} }
} }
} }

View file

@ -568,6 +568,7 @@ fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
"sized" => WellKnownTrait::Sized, "sized" => WellKnownTrait::Sized,
"unpin" => WellKnownTrait::Unpin, "unpin" => WellKnownTrait::Unpin,
"unsize" => WellKnownTrait::Unsize, "unsize" => WellKnownTrait::Unsize,
"tuple_trait" => WellKnownTrait::Tuple,
_ => return None, _ => return None,
}) })
} }
@ -585,6 +586,7 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
WellKnownTrait::FnOnce => "fn_once", WellKnownTrait::FnOnce => "fn_once",
WellKnownTrait::Generator => "generator", WellKnownTrait::Generator => "generator",
WellKnownTrait::Sized => "sized", WellKnownTrait::Sized => "sized",
WellKnownTrait::Tuple => "tuple_trait",
WellKnownTrait::Unpin => "unpin", WellKnownTrait::Unpin => "unpin",
WellKnownTrait::Unsize => "unsize", WellKnownTrait::Unsize => "unsize",
} }

View file

@ -90,14 +90,14 @@ impl Display for ComputedExpr {
ComputedExpr::Literal(l) => match l { ComputedExpr::Literal(l) => match l {
Literal::Int(x, _) => { Literal::Int(x, _) => {
if *x >= 10 { if *x >= 10 {
write!(f, "{} ({:#X})", x, x) write!(f, "{x} ({x:#X})")
} else { } else {
x.fmt(f) x.fmt(f)
} }
} }
Literal::Uint(x, _) => { Literal::Uint(x, _) => {
if *x >= 10 { if *x >= 10 {
write!(f, "{} ({:#X})", x, x) write!(f, "{x} ({x:#X})")
} else { } else {
x.fmt(f) x.fmt(f)
} }
@ -131,7 +131,7 @@ fn scalar_max(scalar: &Scalar) -> i128 {
IntTy::I16 => i16::MAX as i128, IntTy::I16 => i16::MAX as i128,
IntTy::I32 => i32::MAX as i128, IntTy::I32 => i32::MAX as i128,
IntTy::I64 => i64::MAX as i128, IntTy::I64 => i64::MAX as i128,
IntTy::I128 => i128::MAX as i128, IntTy::I128 => i128::MAX,
}, },
Scalar::Uint(x) => match x { Scalar::Uint(x) => match x {
chalk_ir::UintTy::Usize => usize::MAX as i128, chalk_ir::UintTy::Usize => usize::MAX as i128,
@ -139,7 +139,7 @@ fn scalar_max(scalar: &Scalar) -> i128 {
chalk_ir::UintTy::U16 => u16::MAX as i128, chalk_ir::UintTy::U16 => u16::MAX as i128,
chalk_ir::UintTy::U32 => u32::MAX as i128, chalk_ir::UintTy::U32 => u32::MAX as i128,
chalk_ir::UintTy::U64 => u64::MAX as i128, chalk_ir::UintTy::U64 => u64::MAX as i128,
chalk_ir::UintTy::U128 => i128::MAX as i128, // ignore too big u128 for now chalk_ir::UintTy::U128 => i128::MAX, // ignore too big u128 for now
}, },
Scalar::Float(_) => 0, Scalar::Float(_) => 0,
} }
@ -351,15 +351,17 @@ pub fn eval_const(
.infer .infer
.assoc_resolutions_for_expr(expr_id) .assoc_resolutions_for_expr(expr_id)
.ok_or(ConstEvalError::SemanticError("unresolved assoc item"))? .ok_or(ConstEvalError::SemanticError("unresolved assoc item"))?
.0
{ {
hir_def::AssocItemId::FunctionId(_) => { hir_def::AssocItemId::FunctionId(_) => {
Err(ConstEvalError::NotSupported("assoc function")) Err(ConstEvalError::NotSupported("assoc function"))
} }
// FIXME use actual impl for trait assoc const
hir_def::AssocItemId::ConstId(c) => ctx.db.const_eval(c), hir_def::AssocItemId::ConstId(c) => ctx.db.const_eval(c),
hir_def::AssocItemId::TypeAliasId(_) => { hir_def::AssocItemId::TypeAliasId(_) => {
Err(ConstEvalError::NotSupported("assoc type alias")) Err(ConstEvalError::NotSupported("assoc type alias"))
} }
} };
} }
}; };
match pr { match pr {
@ -402,7 +404,7 @@ pub(crate) fn path_to_const(
args_lazy: impl FnOnce() -> Generics, args_lazy: impl FnOnce() -> Generics,
debruijn: DebruijnIndex, debruijn: DebruijnIndex,
) -> Option<Const> { ) -> Option<Const> {
match resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
Some(ValueNs::GenericParam(p)) => { Some(ValueNs::GenericParam(p)) => {
let ty = db.const_param_ty(p); let ty = db.const_param_ty(p);
let args = args_lazy(); let args = args_lazy();
@ -509,10 +511,10 @@ pub(crate) fn const_eval_query_variant(
) )
} }
pub(crate) fn eval_to_const<'a>( pub(crate) fn eval_to_const(
expr: Idx<Expr>, expr: Idx<Expr>,
mode: ParamLoweringMode, mode: ParamLoweringMode,
ctx: &mut InferenceContext<'a>, ctx: &mut InferenceContext<'_>,
args: impl FnOnce() -> Generics, args: impl FnOnce() -> Generics,
debruijn: DebruijnIndex, debruijn: DebruijnIndex,
) -> Const { ) -> Const {

View file

@ -14,7 +14,7 @@ fn check_number(ra_fixture: &str, answer: i128) {
match r { match r {
ComputedExpr::Literal(Literal::Int(r, _)) => assert_eq!(r, answer), ComputedExpr::Literal(Literal::Int(r, _)) => assert_eq!(r, answer),
ComputedExpr::Literal(Literal::Uint(r, _)) => assert_eq!(r, answer as u128), ComputedExpr::Literal(Literal::Uint(r, _)) => assert_eq!(r, answer as u128),
x => panic!("Expected number but found {:?}", x), x => panic!("Expected number but found {x:?}"),
} }
} }
@ -25,7 +25,6 @@ fn eval_goal(ra_fixture: &str) -> Result<ComputedExpr, ConstEvalError> {
let scope = &def_map[module_id.local_id].scope; let scope = &def_map[module_id.local_id].scope;
let const_id = scope let const_id = scope
.declarations() .declarations()
.into_iter()
.find_map(|x| match x { .find_map(|x| match x {
hir_def::ModuleDefId::ConstId(x) => { hir_def::ModuleDefId::ConstId(x) => {
if db.const_data(x).name.as_ref()?.to_string() == "GOAL" { if db.const_data(x).name.as_ref()?.to_string() == "GOAL" {
@ -126,7 +125,7 @@ fn enums() {
assert_eq!(name, "E::A"); assert_eq!(name, "E::A");
assert_eq!(val, 1); assert_eq!(val, 1);
} }
x => panic!("Expected enum but found {:?}", x), x => panic!("Expected enum but found {x:?}"),
} }
} }

View file

@ -3,20 +3,23 @@
use std::sync::Arc; use std::sync::Arc;
use arrayvec::ArrayVec;
use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{ use hir_def::{
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, db::DefDatabase,
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId, expr::ExprId,
layout::{Layout, LayoutError, TargetDataLayout},
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
}; };
use la_arena::ArenaMap; use la_arena::ArenaMap;
use smallvec::SmallVec;
use crate::{ use crate::{
chalk_db, chalk_db,
consteval::{ComputedExpr, ConstEvalError}, consteval::{ComputedExpr, ConstEvalError},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig, Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId, ValueTyDefId,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
@ -57,6 +60,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::lower::field_types_query)] #[salsa::invoke(crate::lower::field_types_query)]
fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>; fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>;
#[salsa::invoke(crate::layout::layout_of_adt_query)]
#[salsa::cycle(crate::layout::layout_of_adt_recover)]
fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>;
#[salsa::invoke(crate::layout::target_data_layout_query)]
fn target_data_layout(&self, krate: CrateId) -> Arc<TargetDataLayout>;
#[salsa::invoke(crate::lower::callable_item_sig)] #[salsa::invoke(crate::lower::callable_item_sig)]
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
@ -92,10 +102,15 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>; fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
/// Collects all crates in the dependency graph that have impls for the /// Collects all crates in the dependency graph that have impls for the
/// given fingerprint. This is only used for primitive types; for /// given fingerprint. This is only used for primitive types and types
/// user-defined types we just look at the crate where the type is defined. /// annotated with `rustc_has_incoherent_inherent_impls`; for other types
#[salsa::invoke(crate::method_resolution::inherent_impl_crates_query)] /// we just look at the crate where the type is defined.
fn inherent_impl_crates(&self, krate: CrateId, fp: TyFingerprint) -> ArrayVec<CrateId, 2>; #[salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)]
fn incoherent_inherent_impl_crates(
&self,
krate: CrateId,
fp: TyFingerprint,
) -> SmallVec<[CrateId; 2]>;
#[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;

View file

@ -12,16 +12,16 @@ pub(crate) mod usefulness;
use chalk_ir::Mutability; use chalk_ir::Mutability;
use hir_def::{ use hir_def::{
adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, HasModule, LocalFieldId, adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
VariantId,
}; };
use hir_expand::name::{name, Name}; use hir_expand::name::Name;
use stdx::{always, never}; use stdx::{always, never};
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
display::{HirDisplay, HirDisplayError, HirFormatter}, display::{HirDisplay, HirDisplayError, HirFormatter},
infer::BindingMode, infer::BindingMode,
lang_items::is_box,
InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
}; };
@ -386,7 +386,7 @@ impl HirDisplay for Pat {
} }
subpattern.hir_fmt(f) subpattern.hir_fmt(f)
} }
PatKind::LiteralBool { value } => write!(f, "{}", value), PatKind::LiteralBool { value } => write!(f, "{value}"),
PatKind::Or { pats } => f.write_joined(pats.iter(), " | "), PatKind::Or { pats } => f.write_joined(pats.iter(), " | "),
} }
} }
@ -405,13 +405,6 @@ where
} }
} }
fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
let owned_box = name![owned_box].to_smol_str();
let krate = adt.module(db.upcast()).krate();
let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
Some(adt) == box_adt
}
pub(crate) trait PatternFoldable: Sized { pub(crate) trait PatternFoldable: Sized {
fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self {
self.super_fold_with(folder) self.super_fold_with(folder)

View file

@ -372,7 +372,7 @@ impl Constructor {
hir_def::AdtId::UnionId(id) => id.into(), hir_def::AdtId::UnionId(id) => id.into(),
} }
} }
_ => panic!("bad constructor {:?} for adt {:?}", self, adt), _ => panic!("bad constructor {self:?} for adt {adt:?}"),
} }
} }

View file

@ -16,7 +16,7 @@ use hir_def::{
path::{Path, PathKind}, path::{Path, PathKind},
type_ref::{ConstScalar, TraitBoundModifier, TypeBound, TypeRef}, type_ref::{ConstScalar, TraitBoundModifier, TypeBound, TypeRef},
visibility::Visibility, visibility::Visibility,
HasModule, ItemContainerId, Lookup, ModuleId, TraitId, HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
}; };
use hir_expand::{hygiene::Hygiene, name::Name}; use hir_expand::{hygiene::Hygiene, name::Name};
use itertools::Itertools; use itertools::Itertools;
@ -35,9 +35,27 @@ use crate::{
TraitRefExt, Ty, TyExt, TyKind, WhereClause, TraitRefExt, Ty, TyExt, TyKind, WhereClause,
}; };
pub trait HirWrite: fmt::Write {
fn start_location_link(&mut self, location: ModuleDefId);
fn end_location_link(&mut self);
}
// String will ignore link metadata
impl HirWrite for String {
fn start_location_link(&mut self, _: ModuleDefId) {}
fn end_location_link(&mut self) {}
}
// `core::Formatter` will ignore metadata
impl HirWrite for fmt::Formatter<'_> {
fn start_location_link(&mut self, _: ModuleDefId) {}
fn end_location_link(&mut self) {}
}
pub struct HirFormatter<'a> { pub struct HirFormatter<'a> {
pub db: &'a dyn HirDatabase, pub db: &'a dyn HirDatabase,
fmt: &'a mut dyn fmt::Write, fmt: &'a mut dyn HirWrite,
buf: String, buf: String,
curr_size: usize, curr_size: usize,
pub(crate) max_size: Option<usize>, pub(crate) max_size: Option<usize>,
@ -45,6 +63,16 @@ pub struct HirFormatter<'a> {
display_target: DisplayTarget, display_target: DisplayTarget,
} }
impl HirFormatter<'_> {
fn start_location_link(&mut self, location: ModuleDefId) {
self.fmt.start_location_link(location);
}
fn end_location_link(&mut self) {
self.fmt.end_location_link();
}
}
pub trait HirDisplay { pub trait HirDisplay {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError>; fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError>;
@ -148,13 +176,13 @@ impl<'a> HirFormatter<'a> {
let mut first = true; let mut first = true;
for e in iter { for e in iter {
if !first { if !first {
write!(self, "{}", sep)?; write!(self, "{sep}")?;
} }
first = false; first = false;
// Abbreviate multiple omitted types with a single ellipsis. // Abbreviate multiple omitted types with a single ellipsis.
if self.should_truncate() { if self.should_truncate() {
return write!(self, "{}", TYPE_HINT_TRUNCATION); return write!(self, "{TYPE_HINT_TRUNCATION}");
} }
e.hir_fmt(self)?; e.hir_fmt(self)?;
@ -245,12 +273,9 @@ pub struct HirDisplayWrapper<'a, T> {
display_target: DisplayTarget, display_target: DisplayTarget,
} }
impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
where pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result<(), HirDisplayError> {
T: HirDisplay, self.t.hir_fmt(&mut HirFormatter {
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.t.hir_fmt(&mut HirFormatter {
db: self.db, db: self.db,
fmt: f, fmt: f,
buf: String::with_capacity(20), buf: String::with_capacity(20),
@ -258,7 +283,16 @@ where
max_size: self.max_size, max_size: self.max_size,
omit_verbose_types: self.omit_verbose_types, omit_verbose_types: self.omit_verbose_types,
display_target: self.display_target, display_target: self.display_target,
}) { })
}
}
impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
where
T: HirDisplay,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.write_to(f) {
Ok(()) => Ok(()), Ok(()) => Ok(()),
Err(HirDisplayError::FmtError) => Err(fmt::Error), Err(HirDisplayError::FmtError) => Err(fmt::Error),
Err(HirDisplayError::DisplaySourceCodeError(_)) => { Err(HirDisplayError::DisplaySourceCodeError(_)) => {
@ -286,7 +320,7 @@ impl<T: HirDisplay + Internable> HirDisplay for Interned<T> {
impl HirDisplay for ProjectionTy { impl HirDisplay for ProjectionTy {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
if f.should_truncate() { if f.should_truncate() {
return write!(f, "{}", TYPE_HINT_TRUNCATION); return write!(f, "{TYPE_HINT_TRUNCATION}");
} }
let trait_ref = self.trait_ref(f.db); let trait_ref = self.trait_ref(f.db);
@ -308,7 +342,7 @@ impl HirDisplay for ProjectionTy {
impl HirDisplay for OpaqueTy { impl HirDisplay for OpaqueTy {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
if f.should_truncate() { if f.should_truncate() {
return write!(f, "{}", TYPE_HINT_TRUNCATION); return write!(f, "{TYPE_HINT_TRUNCATION}");
} }
self.substitution.at(Interner, 0).hir_fmt(f) self.substitution.at(Interner, 0).hir_fmt(f)
@ -351,7 +385,7 @@ impl HirDisplay for BoundVar {
impl HirDisplay for Ty { impl HirDisplay for Ty {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
if f.should_truncate() { if f.should_truncate() {
return write!(f, "{}", TYPE_HINT_TRUNCATION); return write!(f, "{TYPE_HINT_TRUNCATION}");
} }
match self.kind(Interner) { match self.kind(Interner) {
@ -530,6 +564,7 @@ impl HirDisplay for Ty {
} }
} }
TyKind::Adt(AdtId(def_id), parameters) => { TyKind::Adt(AdtId(def_id), parameters) => {
f.start_location_link((*def_id).into());
match f.display_target { match f.display_target {
DisplayTarget::Diagnostics | DisplayTarget::Test => { DisplayTarget::Diagnostics | DisplayTarget::Test => {
let name = match *def_id { let name = match *def_id {
@ -537,7 +572,7 @@ impl HirDisplay for Ty {
hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(), hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
}; };
write!(f, "{}", name)?; write!(f, "{name}")?;
} }
DisplayTarget::SourceCode { module_id } => { DisplayTarget::SourceCode { module_id } => {
if let Some(path) = find_path::find_path( if let Some(path) = find_path::find_path(
@ -546,7 +581,7 @@ impl HirDisplay for Ty {
module_id, module_id,
false, false,
) { ) {
write!(f, "{}", path)?; write!(f, "{path}")?;
} else { } else {
return Err(HirDisplayError::DisplaySourceCodeError( return Err(HirDisplayError::DisplaySourceCodeError(
DisplaySourceCodeError::PathNotFound, DisplaySourceCodeError::PathNotFound,
@ -554,6 +589,7 @@ impl HirDisplay for Ty {
} }
} }
} }
f.end_location_link();
if parameters.len(Interner) > 0 { if parameters.len(Interner) > 0 {
let parameters_to_write = if f.display_target.is_source_code() let parameters_to_write = if f.display_target.is_source_code()
@ -701,7 +737,7 @@ impl HirDisplay for Ty {
if sig.params().is_empty() { if sig.params().is_empty() {
write!(f, "||")?; write!(f, "||")?;
} else if f.should_truncate() { } else if f.should_truncate() {
write!(f, "|{}|", TYPE_HINT_TRUNCATION)?; write!(f, "|{TYPE_HINT_TRUNCATION}|")?;
} else { } else {
write!(f, "|")?; write!(f, "|")?;
f.write_joined(sig.params(), ", ")?; f.write_joined(sig.params(), ", ")?;
@ -892,7 +928,7 @@ pub fn write_bounds_like_dyn_trait_with_prefix(
default_sized: SizedByDefault, default_sized: SizedByDefault,
f: &mut HirFormatter<'_>, f: &mut HirFormatter<'_>,
) -> Result<(), HirDisplayError> { ) -> Result<(), HirDisplayError> {
write!(f, "{}", prefix)?; write!(f, "{prefix}")?;
if !predicates.is_empty() if !predicates.is_empty()
|| predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. }) || predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. })
{ {
@ -1020,7 +1056,7 @@ fn fmt_trait_ref(
use_as: bool, use_as: bool,
) -> Result<(), HirDisplayError> { ) -> Result<(), HirDisplayError> {
if f.should_truncate() { if f.should_truncate() {
return write!(f, "{}", TYPE_HINT_TRUNCATION); return write!(f, "{TYPE_HINT_TRUNCATION}");
} }
tr.self_type_parameter(Interner).hir_fmt(f)?; tr.self_type_parameter(Interner).hir_fmt(f)?;
@ -1047,7 +1083,7 @@ impl HirDisplay for TraitRef {
impl HirDisplay for WhereClause { impl HirDisplay for WhereClause {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
if f.should_truncate() { if f.should_truncate() {
return write!(f, "{}", TYPE_HINT_TRUNCATION); return write!(f, "{TYPE_HINT_TRUNCATION}");
} }
match self { match self {
@ -1098,7 +1134,6 @@ impl HirDisplay for LifetimeData {
write!(f, "{}", param_data.name) write!(f, "{}", param_data.name)
} }
LifetimeData::Static => write!(f, "'static"), LifetimeData::Static => write!(f, "'static"),
LifetimeData::Empty(_) => Ok(()),
LifetimeData::Erased => Ok(()), LifetimeData::Erased => Ok(()),
LifetimeData::Phantom(_, _) => Ok(()), LifetimeData::Phantom(_, _) => Ok(()),
} }
@ -1162,7 +1197,7 @@ impl HirDisplay for TypeRef {
hir_def::type_ref::Mutability::Shared => "*const ", hir_def::type_ref::Mutability::Shared => "*const ",
hir_def::type_ref::Mutability::Mut => "*mut ", hir_def::type_ref::Mutability::Mut => "*mut ",
}; };
write!(f, "{}", mutability)?; write!(f, "{mutability}")?;
inner.hir_fmt(f)?; inner.hir_fmt(f)?;
} }
TypeRef::Reference(inner, lifetime, mutability) => { TypeRef::Reference(inner, lifetime, mutability) => {
@ -1174,13 +1209,13 @@ impl HirDisplay for TypeRef {
if let Some(lifetime) = lifetime { if let Some(lifetime) = lifetime {
write!(f, "{} ", lifetime.name)?; write!(f, "{} ", lifetime.name)?;
} }
write!(f, "{}", mutability)?; write!(f, "{mutability}")?;
inner.hir_fmt(f)?; inner.hir_fmt(f)?;
} }
TypeRef::Array(inner, len) => { TypeRef::Array(inner, len) => {
write!(f, "[")?; write!(f, "[")?;
inner.hir_fmt(f)?; inner.hir_fmt(f)?;
write!(f, "; {}]", len)?; write!(f, "; {len}]")?;
} }
TypeRef::Slice(inner) => { TypeRef::Slice(inner) => {
write!(f, "[")?; write!(f, "[")?;
@ -1197,7 +1232,7 @@ impl HirDisplay for TypeRef {
for index in 0..function_parameters.len() { for index in 0..function_parameters.len() {
let (param_name, param_type) = &function_parameters[index]; let (param_name, param_type) = &function_parameters[index];
if let Some(name) = param_name { if let Some(name) = param_name {
write!(f, "{}: ", name)?; write!(f, "{name}: ")?;
} }
param_type.hir_fmt(f)?; param_type.hir_fmt(f)?;
@ -1373,7 +1408,7 @@ impl HirDisplay for hir_def::path::GenericArg {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
match self { match self {
hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f), hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f),
hir_def::path::GenericArg::Const(c) => write!(f, "{}", c), hir_def::path::GenericArg::Const(c) => write!(f, "{c}"),
hir_def::path::GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name), hir_def::path::GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
} }
} }

View file

@ -19,10 +19,11 @@ use std::sync::Arc;
use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
use hir_def::{ use hir_def::{
body::Body, body::Body,
builtin_type::BuiltinType, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData}, data::{ConstData, StaticData},
expr::{BindingAnnotation, ExprId, PatId}, expr::{BindingAnnotation, ExprId, ExprOrPatId, PatId},
lang_item::LangItemTarget, lang_item::LangItemTarget,
layout::Integer,
path::{path, Path}, path::{path, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef, type_ref::TypeRef,
@ -33,7 +34,7 @@ use hir_expand::name::{name, Name};
use itertools::Either; use itertools::Either;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use stdx::{always, impl_from}; use stdx::always;
use crate::{ use crate::{
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany, db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
@ -70,8 +71,26 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
DefWithBodyId::VariantId(v) => { DefWithBodyId::VariantId(v) => {
ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() { ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
Either::Left(builtin) => BuiltinType::Int(builtin), hir_def::layout::IntegerType::Pointer(signed) => match signed {
Either::Right(builtin) => BuiltinType::Uint(builtin), true => BuiltinType::Int(BuiltinInt::Isize),
false => BuiltinType::Uint(BuiltinUint::Usize),
},
hir_def::layout::IntegerType::Fixed(size, signed) => match signed {
true => BuiltinType::Int(match size {
Integer::I8 => BuiltinInt::I8,
Integer::I16 => BuiltinInt::I16,
Integer::I32 => BuiltinInt::I32,
Integer::I64 => BuiltinInt::I64,
Integer::I128 => BuiltinInt::I128,
}),
false => BuiltinType::Uint(match size {
Integer::I8 => BuiltinUint::U8,
Integer::I16 => BuiltinUint::U16,
Integer::I32 => BuiltinUint::U32,
Integer::I64 => BuiltinUint::U64,
Integer::I128 => BuiltinUint::U128,
}),
},
}); });
} }
} }
@ -101,13 +120,6 @@ pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> T
table.resolve_completely(ty_with_vars) table.resolve_completely(ty_with_vars)
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
enum ExprOrPatId {
ExprId(ExprId),
PatId(PatId),
}
impl_from!(ExprId, PatId for ExprOrPatId);
/// Binding modes inferred for patterns. /// Binding modes inferred for patterns.
/// <https://doc.rust-lang.org/reference/patterns.html#binding-modes> /// <https://doc.rust-lang.org/reference/patterns.html#binding-modes>
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -189,6 +201,8 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum InferenceDiagnostic { pub enum InferenceDiagnostic {
NoSuchField { expr: ExprId }, NoSuchField { expr: ExprId },
PrivateField { expr: ExprId, field: FieldId },
PrivateAssocItem { id: ExprOrPatId, item: AssocItemId },
BreakOutsideOfLoop { expr: ExprId, is_break: bool }, BreakOutsideOfLoop { expr: ExprId, is_break: bool },
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize }, MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
} }
@ -330,7 +344,7 @@ pub struct InferenceResult {
/// For each struct literal or pattern, records the variant it resolves to. /// For each struct literal or pattern, records the variant it resolves to.
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
/// For each associated item record what it resolves to /// For each associated item record what it resolves to
assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, assoc_resolutions: FxHashMap<ExprOrPatId, (AssocItemId, Substitution)>,
pub diagnostics: Vec<InferenceDiagnostic>, pub diagnostics: Vec<InferenceDiagnostic>,
pub type_of_expr: ArenaMap<ExprId, Ty>, pub type_of_expr: ArenaMap<ExprId, Ty>,
/// For each pattern record the type it resolves to. /// For each pattern record the type it resolves to.
@ -360,11 +374,11 @@ impl InferenceResult {
pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantId> { pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantId> {
self.variant_resolutions.get(&id.into()).copied() self.variant_resolutions.get(&id.into()).copied()
} }
pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<AssocItemId> { pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<(AssocItemId, Substitution)> {
self.assoc_resolutions.get(&id.into()).copied() self.assoc_resolutions.get(&id.into()).cloned()
} }
pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<AssocItemId> { pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(AssocItemId, Substitution)> {
self.assoc_resolutions.get(&id.into()).copied() self.assoc_resolutions.get(&id.into()).cloned()
} }
pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> { pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
self.type_mismatches.get(&expr.into()) self.type_mismatches.get(&expr.into())
@ -484,7 +498,7 @@ impl<'a> InferenceContext<'a> {
result: InferenceResult::default(), result: InferenceResult::default(),
table: unify::InferenceTable::new(db, trait_env.clone()), table: unify::InferenceTable::new(db, trait_env.clone()),
trait_env, trait_env,
return_ty: TyKind::Error.intern(Interner), // set in collect_fn_signature return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
resume_yield_tys: None, resume_yield_tys: None,
db, db,
owner, owner,
@ -498,6 +512,8 @@ impl<'a> InferenceContext<'a> {
fn resolve_all(self) -> InferenceResult { fn resolve_all(self) -> InferenceResult {
let InferenceContext { mut table, mut result, .. } = self; let InferenceContext { mut table, mut result, .. } = self;
table.fallback_if_possible();
// FIXME resolve obligations as well (use Guidance if necessary) // FIXME resolve obligations as well (use Guidance if necessary)
table.resolve_obligations_as_possible(); table.resolve_obligations_as_possible();
@ -516,6 +532,9 @@ impl<'a> InferenceContext<'a> {
for (_, subst) in result.method_resolutions.values_mut() { for (_, subst) in result.method_resolutions.values_mut() {
*subst = table.resolve_completely(subst.clone()); *subst = table.resolve_completely(subst.clone());
} }
for (_, subst) in result.assoc_resolutions.values_mut() {
*subst = table.resolve_completely(subst.clone());
}
for adjustment in result.expr_adjustments.values_mut().flatten() { for adjustment in result.expr_adjustments.values_mut().flatten() {
adjustment.target = table.resolve_completely(adjustment.target.clone()); adjustment.target = table.resolve_completely(adjustment.target.clone());
} }
@ -537,8 +556,20 @@ impl<'a> InferenceContext<'a> {
let data = self.db.function_data(func); let data = self.db.function_data(func);
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
.with_impl_trait_mode(ImplTraitLoweringMode::Param); .with_impl_trait_mode(ImplTraitLoweringMode::Param);
let param_tys = let mut param_tys =
data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::<Vec<_>>(); data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
// Check if function contains a va_list, if it does then we append it to the parameter types
// that are collected from the function data
if data.is_varargs() {
let va_list_ty = match self.resolve_va_list() {
Some(va_list) => TyBuilder::adt(self.db, va_list)
.fill_with_defaults(self.db, || self.table.new_type_var())
.build(),
None => self.err_ty(),
};
param_tys.push(va_list_ty)
}
for (ty, pat) in param_tys.into_iter().zip(self.body.params.iter()) { for (ty, pat) in param_tys.into_iter().zip(self.body.params.iter()) {
let ty = self.insert_type_vars(ty); let ty = self.insert_type_vars(ty);
let ty = self.normalize_associated_types_in(ty); let ty = self.normalize_associated_types_in(ty);
@ -551,14 +582,17 @@ impl<'a> InferenceContext<'a> {
} else { } else {
&*data.ret_type &*data.ret_type
}; };
let return_ty = self.make_ty_with_mode(return_ty, ImplTraitLoweringMode::Opaque);
self.return_ty = return_ty;
if let Some(rpits) = self.db.return_type_impl_traits(func) { let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
let return_ty = ctx.lower_ty(return_ty);
let return_ty = self.insert_type_vars(return_ty);
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
// RPIT opaque types use substitution of their parent function. // RPIT opaque types use substitution of their parent function.
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
self.return_ty = fold_tys( fold_tys(
self.return_ty.clone(), return_ty,
|ty, _| { |ty, _| {
let opaque_ty_id = match ty.kind(Interner) { let opaque_ty_id = match ty.kind(Interner) {
TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id, TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
@ -579,14 +613,18 @@ impl<'a> InferenceContext<'a> {
let (var_predicate, binders) = predicate let (var_predicate, binders) = predicate
.substitute(Interner, &var_subst) .substitute(Interner, &var_subst)
.into_value_and_skipped_binders(); .into_value_and_skipped_binders();
always!(binders.len(Interner) == 0); // quantified where clauses not yet handled always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
self.push_obligation(var_predicate.cast(Interner)); self.push_obligation(var_predicate.cast(Interner));
} }
var var
}, },
DebruijnIndex::INNERMOST, DebruijnIndex::INNERMOST,
); )
} } else {
return_ty
};
self.return_ty = self.normalize_associated_types_in(return_ty);
} }
fn infer_body(&mut self) { fn infer_body(&mut self) {
@ -609,8 +647,8 @@ impl<'a> InferenceContext<'a> {
self.result.variant_resolutions.insert(id, variant); self.result.variant_resolutions.insert(id, variant);
} }
fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItemId) { fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItemId, subs: Substitution) {
self.result.assoc_resolutions.insert(id, item); self.result.assoc_resolutions.insert(id, (item, subs));
} }
fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { fn write_pat_ty(&mut self, pat: PatId, ty: Ty) {
@ -621,23 +659,14 @@ impl<'a> InferenceContext<'a> {
self.result.diagnostics.push(diagnostic); self.result.diagnostics.push(diagnostic);
} }
fn make_ty_with_mode( fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
&mut self,
type_ref: &TypeRef,
impl_trait_mode: ImplTraitLoweringMode,
) -> Ty {
// FIXME use right resolver for block // FIXME use right resolver for block
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
.with_impl_trait_mode(impl_trait_mode);
let ty = ctx.lower_ty(type_ref); let ty = ctx.lower_ty(type_ref);
let ty = self.insert_type_vars(ty); let ty = self.insert_type_vars(ty);
self.normalize_associated_types_in(ty) self.normalize_associated_types_in(ty)
} }
fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
self.make_ty_with_mode(type_ref, ImplTraitLoweringMode::Disallowed)
}
fn err_ty(&self) -> Ty { fn err_ty(&self) -> Ty {
self.result.standard_types.unknown.clone() self.result.standard_types.unknown.clone()
} }
@ -656,7 +685,7 @@ impl<'a> InferenceContext<'a> {
} }
} }
/// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
match ty.kind(Interner) { match ty.kind(Interner) {
TyKind::Error => self.table.new_type_var(), TyKind::Error => self.table.new_type_var(),
@ -983,6 +1012,11 @@ impl<'a> InferenceContext<'a> {
let trait_ = self.resolve_ops_index()?; let trait_ = self.resolve_ops_index()?;
self.db.trait_data(trait_).associated_type_by_name(&name![Output]) self.db.trait_data(trait_).associated_type_by_name(&name![Output])
} }
fn resolve_va_list(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(name![va_list])?.as_struct()?;
Some(struct_.into())
}
} }
/// When inferring an expression, we propagate downward whatever type hint we /// When inferring an expression, we propagate downward whatever type hint we

View file

@ -1,13 +1,12 @@
//! Type inference for expressions. //! Type inference for expressions.
use std::{ use std::{
collections::hash_map::Entry,
iter::{repeat, repeat_with}, iter::{repeat, repeat_with},
mem, mem,
}; };
use chalk_ir::{ use chalk_ir::{
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind,
}; };
use hir_def::{ use hir_def::{
expr::{ expr::{
@ -35,8 +34,8 @@ use crate::{
primitive::{self, UintTy}, primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id, static_lifetime, to_chalk_trait_id,
utils::{generics, Generics}, utils::{generics, Generics},
AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
}; };
use super::{ use super::{
@ -152,11 +151,20 @@ impl<'a> InferenceContext<'a> {
.1 .1
} }
Expr::TryBlock { body } => { Expr::TryBlock { body } => {
self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| { // The type that is returned from the try block
let _inner = this.infer_expr(*body, expected); let try_ty = self.table.new_type_var();
if let Some(ty) = expected.only_has_type(&mut self.table) {
self.unify(&try_ty, &ty);
}
// The ok-ish type that is expected from the last expression
let ok_ty = self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_ok());
self.with_breakable_ctx(BreakableKind::Block, ok_ty.clone(), None, |this| {
this.infer_expr(*body, &Expectation::has_type(ok_ty));
}); });
// FIXME should be std::result::Result<{inner}, _>
self.err_ty() try_ty
} }
Expr::Async { body } => { Expr::Async { body } => {
let ret_ty = self.table.new_type_var(); let ret_ty = self.table.new_type_var();
@ -326,6 +334,7 @@ impl<'a> InferenceContext<'a> {
let (param_tys, ret_ty) = match res { let (param_tys, ret_ty) = match res {
Some(res) => { Some(res) => {
let adjustments = auto_deref_adjust_steps(&derefs); let adjustments = auto_deref_adjust_steps(&derefs);
// FIXME: Handle call adjustments for Fn/FnMut
self.write_expr_adj(*callee, adjustments); self.write_expr_adj(*callee, adjustments);
res res
} }
@ -465,6 +474,12 @@ impl<'a> InferenceContext<'a> {
TyKind::Error.intern(Interner) TyKind::Error.intern(Interner)
} }
} }
Expr::Yeet { expr } => {
if let &Some(expr) = expr {
self.infer_expr_inner(expr, &Expectation::None);
}
TyKind::Never.intern(Interner)
}
Expr::RecordLit { path, fields, spread, .. } => { Expr::RecordLit { path, fields, spread, .. } => {
let (ty, def_id) = self.resolve_variant(path.as_deref(), false); let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
if let Some(variant) = def_id { if let Some(variant) = def_id {
@ -506,6 +521,7 @@ impl<'a> InferenceContext<'a> {
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none()); let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty); let mut autoderef = Autoderef::new(&mut self.table, receiver_ty);
let mut private_field = None;
let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| { let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
let (field_id, parameters) = match derefed_ty.kind(Interner) { let (field_id, parameters) = match derefed_ty.kind(Interner) {
TyKind::Tuple(_, substs) => { TyKind::Tuple(_, substs) => {
@ -532,13 +548,8 @@ impl<'a> InferenceContext<'a> {
let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id] let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
.is_visible_from(self.db.upcast(), self.resolver.module()); .is_visible_from(self.db.upcast(), self.resolver.module());
if !is_visible { if !is_visible {
// Write down the first field resolution even if it is not visible if private_field.is_none() {
// This aids IDE features for private fields like goto def and in private_field = Some(field_id);
// case of autoderef finding an applicable field, this will be
// overwritten in a following cycle
if let Entry::Vacant(entry) = self.result.field_resolutions.entry(tgt_expr)
{
entry.insert(field_id);
} }
return None; return None;
} }
@ -557,7 +568,17 @@ impl<'a> InferenceContext<'a> {
let ty = self.normalize_associated_types_in(ty); let ty = self.normalize_associated_types_in(ty);
ty ty
} }
_ => self.err_ty(), _ => {
// Write down the first private field resolution if we found no field
// This aids IDE features for private fields like goto def
if let Some(field) = private_field {
self.result.field_resolutions.insert(tgt_expr, field);
self.result
.diagnostics
.push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
}
self.err_ty()
}
}; };
ty ty
} }
@ -940,7 +961,7 @@ impl<'a> InferenceContext<'a> {
Expr::RecordLit { path, fields, .. } => { Expr::RecordLit { path, fields, .. } => {
let subs = fields.iter().map(|f| (f.name.clone(), f.expr)); let subs = fields.iter().map(|f| (f.name.clone(), f.expr));
self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs.into(), subs) self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs, subs)
} }
Expr::Underscore => rhs_ty.clone(), Expr::Underscore => rhs_ty.clone(),
_ => { _ => {
@ -1018,14 +1039,38 @@ impl<'a> InferenceContext<'a> {
self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone())); self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()));
let ret_ty = match method_ty.callable_sig(self.db) { let ret_ty = match method_ty.callable_sig(self.db) {
Some(sig) => sig.ret().clone(), Some(sig) => {
let p_left = &sig.params()[0];
if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. }) {
if let &TyKind::Ref(mtbl, _, _) = p_left.kind(Interner) {
self.write_expr_adj(
lhs,
vec![Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mtbl)),
target: p_left.clone(),
}],
);
}
}
let p_right = &sig.params()[1];
if matches!(op, BinaryOp::CmpOp(..)) {
if let &TyKind::Ref(mtbl, _, _) = p_right.kind(Interner) {
self.write_expr_adj(
rhs,
vec![Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mtbl)),
target: p_right.clone(),
}],
);
}
}
sig.ret().clone()
}
None => self.err_ty(), None => self.err_ty(),
}; };
let ret_ty = self.normalize_associated_types_in(ret_ty); let ret_ty = self.normalize_associated_types_in(ret_ty);
// FIXME: record autoref adjustments
// use knowledge of built-in binary ops, which can sometimes help inference // use knowledge of built-in binary ops, which can sometimes help inference
if let Some(builtin_rhs) = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone()) { if let Some(builtin_rhs) = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone()) {
self.unify(&builtin_rhs, &rhs_ty); self.unify(&builtin_rhs, &rhs_ty);
@ -1122,20 +1167,26 @@ impl<'a> InferenceContext<'a> {
let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
let resolved = method_resolution::lookup_method( let resolved = method_resolution::lookup_method(
&canonicalized_receiver.value,
self.db, self.db,
&canonicalized_receiver.value,
self.trait_env.clone(), self.trait_env.clone(),
&traits_in_scope, &traits_in_scope,
VisibleFromModule::Filter(self.resolver.module()), VisibleFromModule::Filter(self.resolver.module()),
method_name, method_name,
); );
let (receiver_ty, method_ty, substs) = match resolved { let (receiver_ty, method_ty, substs) = match resolved {
Some((adjust, func)) => { Some((adjust, func, visible)) => {
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty); let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
let generics = generics(self.db.upcast(), func.into()); let generics = generics(self.db.upcast(), func.into());
let substs = self.substs_for_method_call(generics, generic_args); let substs = self.substs_for_method_call(generics, generic_args);
self.write_expr_adj(receiver, adjustments); self.write_expr_adj(receiver, adjustments);
self.write_method_resolution(tgt_expr, func, substs.clone()); self.write_method_resolution(tgt_expr, func, substs.clone());
if !visible {
self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem {
id: tgt_expr.into(),
item: func.into(),
})
}
(ty, self.db.value_ty(func.into()), substs) (ty, self.db.value_ty(func.into()), substs)
} }
None => ( None => (
@ -1309,7 +1360,7 @@ impl<'a> InferenceContext<'a> {
ty, ty,
c, c,
ParamLoweringMode::Placeholder, ParamLoweringMode::Placeholder,
|| generics(this.db.upcast(), (&this.resolver).generic_def().unwrap()), || generics(this.db.upcast(), this.resolver.generic_def().unwrap()),
DebruijnIndex::INNERMOST, DebruijnIndex::INNERMOST,
) )
}, },

View file

@ -153,7 +153,7 @@ impl<'a> InferenceContext<'a> {
) -> Ty { ) -> Ty {
let mut expected = self.resolve_ty_shallow(expected); let mut expected = self.resolve_ty_shallow(expected);
if is_non_ref_pat(&self.body, pat) { if is_non_ref_pat(self.body, pat) {
let mut pat_adjustments = Vec::new(); let mut pat_adjustments = Vec::new();
while let Some((inner, _lifetime, mutability)) = expected.as_reference() { while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
pat_adjustments.push(expected.clone()); pat_adjustments.push(expected.clone());
@ -220,7 +220,7 @@ impl<'a> InferenceContext<'a> {
), ),
Pat::Record { path: p, args: fields, ellipsis: _ } => { Pat::Record { path: p, args: fields, ellipsis: _ } => {
let subs = fields.iter().map(|f| (f.name.clone(), f.pat)); let subs = fields.iter().map(|f| (f.name.clone(), f.pat));
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat.into(), subs) self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
} }
Pat::Path(path) => { Pat::Path(path) => {
// FIXME use correct resolver for the surrounding expression // FIXME use correct resolver for the surrounding expression

View file

@ -7,13 +7,15 @@ use hir_def::{
AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup, AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use stdx::never;
use crate::{ use crate::{
builder::ParamKind, builder::ParamKind,
consteval, consteval,
method_resolution::{self, VisibleFromModule}, method_resolution::{self, VisibleFromModule},
utils::generics, utils::generics,
Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId, InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
ValueTyDefId,
}; };
use super::{ExprOrPatId, InferenceContext, TraitRef}; use super::{ExprOrPatId, InferenceContext, TraitRef};
@ -212,7 +214,7 @@ impl<'a> InferenceContext<'a> {
AssocItemId::TypeAliasId(_) => unreachable!(), AssocItemId::TypeAliasId(_) => unreachable!(),
}; };
self.write_assoc_resolution(id, item); self.write_assoc_resolution(id, item, trait_ref.substitution.clone());
Some((def, Some(trait_ref.substitution))) Some((def, Some(trait_ref.substitution)))
} }
@ -233,7 +235,8 @@ impl<'a> InferenceContext<'a> {
let canonical_ty = self.canonicalize(ty.clone()); let canonical_ty = self.canonicalize(ty.clone());
let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
method_resolution::iterate_method_candidates( let mut not_visible = None;
let res = method_resolution::iterate_method_candidates(
&canonical_ty.value, &canonical_ty.value,
self.db, self.db,
self.table.trait_env.clone(), self.table.trait_env.clone(),
@ -241,7 +244,7 @@ impl<'a> InferenceContext<'a> {
VisibleFromModule::Filter(self.resolver.module()), VisibleFromModule::Filter(self.resolver.module()),
Some(name), Some(name),
method_resolution::LookupMode::Path, method_resolution::LookupMode::Path,
move |_ty, item| { |_ty, item, visible| {
let (def, container) = match item { let (def, container) = match item {
AssocItemId::FunctionId(f) => { AssocItemId::FunctionId(f) => {
(ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container) (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container)
@ -259,7 +262,7 @@ impl<'a> InferenceContext<'a> {
let impl_self_ty = let impl_self_ty =
self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs); self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
self.unify(&impl_self_ty, &ty); self.unify(&impl_self_ty, &ty);
Some(impl_substs) impl_substs
} }
ItemContainerId::TraitId(trait_) => { ItemContainerId::TraitId(trait_) => {
// we're picking this method // we're picking this method
@ -268,15 +271,32 @@ impl<'a> InferenceContext<'a> {
.fill_with_inference_vars(&mut self.table) .fill_with_inference_vars(&mut self.table)
.build(); .build();
self.push_obligation(trait_ref.clone().cast(Interner)); self.push_obligation(trait_ref.clone().cast(Interner));
Some(trait_ref.substitution) trait_ref.substitution
}
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
never!("assoc item contained in module/extern block");
return None;
} }
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
}; };
self.write_assoc_resolution(id, item); if visible {
Some((def, substs)) Some((def, item, Some(substs), true))
} else {
if not_visible.is_none() {
not_visible = Some((def, item, Some(substs), false));
}
None
}
}, },
) );
let res = res.or(not_visible);
if let Some((_, item, Some(ref substs), visible)) = res {
self.write_assoc_resolution(id, item, substs.clone());
if !visible {
self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item })
}
}
res.map(|(def, _, substs, _)| (def, substs))
} }
fn resolve_enum_variant_on_ty( fn resolve_enum_variant_on_ty(

View file

@ -1,6 +1,6 @@
//! Unification and canonicalization logic. //! Unification and canonicalization logic.
use std::{fmt, mem, sync::Arc}; use std::{fmt, iter, mem, sync::Arc};
use chalk_ir::{ use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy, cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy,
@ -128,9 +128,13 @@ pub(crate) fn unify(
)) ))
} }
#[derive(Copy, Clone, Debug)] bitflags::bitflags! {
pub(crate) struct TypeVariableData { #[derive(Default)]
diverging: bool, pub(crate) struct TypeVariableFlags: u8 {
const DIVERGING = 1 << 0;
const INTEGER = 1 << 1;
const FLOAT = 1 << 2;
}
} }
type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>; type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
@ -140,14 +144,14 @@ pub(crate) struct InferenceTable<'a> {
pub(crate) db: &'a dyn HirDatabase, pub(crate) db: &'a dyn HirDatabase,
pub(crate) trait_env: Arc<TraitEnvironment>, pub(crate) trait_env: Arc<TraitEnvironment>,
var_unification_table: ChalkInferenceTable, var_unification_table: ChalkInferenceTable,
type_variable_table: Vec<TypeVariableData>, type_variable_table: Vec<TypeVariableFlags>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>, pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
} }
pub(crate) struct InferenceTableSnapshot { pub(crate) struct InferenceTableSnapshot {
var_table_snapshot: chalk_solve::infer::InferenceSnapshot<Interner>, var_table_snapshot: chalk_solve::infer::InferenceSnapshot<Interner>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>, pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
type_variable_table_snapshot: Vec<TypeVariableData>, type_variable_table_snapshot: Vec<TypeVariableFlags>,
} }
impl<'a> InferenceTable<'a> { impl<'a> InferenceTable<'a> {
@ -169,19 +173,19 @@ impl<'a> InferenceTable<'a> {
/// result. /// result.
pub(super) fn propagate_diverging_flag(&mut self) { pub(super) fn propagate_diverging_flag(&mut self) {
for i in 0..self.type_variable_table.len() { for i in 0..self.type_variable_table.len() {
if !self.type_variable_table[i].diverging { if !self.type_variable_table[i].contains(TypeVariableFlags::DIVERGING) {
continue; continue;
} }
let v = InferenceVar::from(i as u32); let v = InferenceVar::from(i as u32);
let root = self.var_unification_table.inference_var_root(v); let root = self.var_unification_table.inference_var_root(v);
if let Some(data) = self.type_variable_table.get_mut(root.index() as usize) { if let Some(data) = self.type_variable_table.get_mut(root.index() as usize) {
data.diverging = true; *data |= TypeVariableFlags::DIVERGING;
} }
} }
} }
pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) {
self.type_variable_table[iv.index() as usize].diverging = diverging; self.type_variable_table[iv.index() as usize].set(TypeVariableFlags::DIVERGING, diverging);
} }
fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty {
@ -189,7 +193,7 @@ impl<'a> InferenceTable<'a> {
_ if self _ if self
.type_variable_table .type_variable_table
.get(iv.index() as usize) .get(iv.index() as usize)
.map_or(false, |data| data.diverging) => .map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)) =>
{ {
TyKind::Never TyKind::Never
} }
@ -247,10 +251,8 @@ impl<'a> InferenceTable<'a> {
} }
fn extend_type_variable_table(&mut self, to_index: usize) { fn extend_type_variable_table(&mut self, to_index: usize) {
self.type_variable_table.extend( let count = to_index - self.type_variable_table.len() + 1;
(0..1 + to_index - self.type_variable_table.len()) self.type_variable_table.extend(iter::repeat(TypeVariableFlags::default()).take(count));
.map(|_| TypeVariableData { diverging: false }),
);
} }
fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty {
@ -258,7 +260,15 @@ impl<'a> InferenceTable<'a> {
// Chalk might have created some type variables for its own purposes that we don't know about... // Chalk might have created some type variables for its own purposes that we don't know about...
self.extend_type_variable_table(var.index() as usize); self.extend_type_variable_table(var.index() as usize);
assert_eq!(var.index() as usize, self.type_variable_table.len() - 1); assert_eq!(var.index() as usize, self.type_variable_table.len() - 1);
self.type_variable_table[var.index() as usize].diverging = diverging; let flags = self.type_variable_table.get_mut(var.index() as usize).unwrap();
if diverging {
*flags |= TypeVariableFlags::DIVERGING;
}
if matches!(kind, TyVariableKind::Integer) {
*flags |= TypeVariableFlags::INTEGER;
} else if matches!(kind, TyVariableKind::Float) {
*flags |= TypeVariableFlags::FLOAT;
}
var.to_ty_with_kind(Interner, kind) var.to_ty_with_kind(Interner, kind)
} }
@ -340,6 +350,51 @@ impl<'a> InferenceTable<'a> {
self.resolve_with_fallback(t, &|_, _, d, _| d) self.resolve_with_fallback(t, &|_, _, d, _| d)
} }
/// Apply a fallback to unresolved scalar types. Integer type variables and float type
/// variables are replaced with i32 and f64, respectively.
///
/// This method is only intended to be called just before returning inference results (i.e. in
/// `InferenceContext::resolve_all()`).
///
/// FIXME: This method currently doesn't apply fallback to unconstrained general type variables
/// whereas rustc replaces them with `()` or `!`.
pub(super) fn fallback_if_possible(&mut self) {
let int_fallback = TyKind::Scalar(Scalar::Int(IntTy::I32)).intern(Interner);
let float_fallback = TyKind::Scalar(Scalar::Float(FloatTy::F64)).intern(Interner);
let scalar_vars: Vec<_> = self
.type_variable_table
.iter()
.enumerate()
.filter_map(|(index, flags)| {
let kind = if flags.contains(TypeVariableFlags::INTEGER) {
TyVariableKind::Integer
} else if flags.contains(TypeVariableFlags::FLOAT) {
TyVariableKind::Float
} else {
return None;
};
// FIXME: This is not really the nicest way to get `InferenceVar`s. Can we get them
// without directly constructing them from `index`?
let var = InferenceVar::from(index as u32).to_ty(Interner, kind);
Some(var)
})
.collect();
for var in scalar_vars {
let maybe_resolved = self.resolve_ty_shallow(&var);
if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) {
let fallback = match kind {
TyVariableKind::Integer => &int_fallback,
TyVariableKind::Float => &float_fallback,
TyVariableKind::General => unreachable!(),
};
self.unify(&var, fallback);
}
}
}
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool { pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
let result = match self.try_unify(ty1, ty2) { let result = match self.try_unify(ty1, ty2) {

View file

@ -143,7 +143,7 @@ impl chalk_ir::interner::Interner for Interner {
fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
let goal_data = goal.data(Interner); let goal_data = goal.data(Interner);
Some(write!(fmt, "{:?}", goal_data)) Some(write!(fmt, "{goal_data:?}"))
} }
fn debug_goals( fn debug_goals(
@ -228,7 +228,7 @@ impl chalk_ir::interner::Interner for Interner {
Interned::new(InternedWrapper(chalk_ir::TyData { kind, flags })) Interned::new(InternedWrapper(chalk_ir::TyData { kind, flags }))
} }
fn ty_data<'a>(self, ty: &'a Self::InternedType) -> &'a chalk_ir::TyData<Self> { fn ty_data(self, ty: &Self::InternedType) -> &chalk_ir::TyData<Self> {
&ty.0 &ty.0
} }
@ -236,10 +236,7 @@ impl chalk_ir::interner::Interner for Interner {
Interned::new(InternedWrapper(lifetime)) Interned::new(InternedWrapper(lifetime))
} }
fn lifetime_data<'a>( fn lifetime_data(self, lifetime: &Self::InternedLifetime) -> &chalk_ir::LifetimeData<Self> {
self,
lifetime: &'a Self::InternedLifetime,
) -> &'a chalk_ir::LifetimeData<Self> {
&lifetime.0 &lifetime.0
} }
@ -247,7 +244,7 @@ impl chalk_ir::interner::Interner for Interner {
Interned::new(InternedWrapper(constant)) Interned::new(InternedWrapper(constant))
} }
fn const_data<'a>(self, constant: &'a Self::InternedConst) -> &'a chalk_ir::ConstData<Self> { fn const_data(self, constant: &Self::InternedConst) -> &chalk_ir::ConstData<Self> {
&constant.0 &constant.0
} }
@ -267,10 +264,10 @@ impl chalk_ir::interner::Interner for Interner {
parameter parameter
} }
fn generic_arg_data<'a>( fn generic_arg_data(
self, self,
parameter: &'a Self::InternedGenericArg, parameter: &Self::InternedGenericArg,
) -> &'a chalk_ir::GenericArgData<Self> { ) -> &chalk_ir::GenericArgData<Self> {
parameter parameter
} }
@ -285,11 +282,11 @@ impl chalk_ir::interner::Interner for Interner {
data.into_iter().collect() data.into_iter().collect()
} }
fn goal_data<'a>(self, goal: &'a Self::InternedGoal) -> &'a GoalData<Self> { fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData<Self> {
goal goal
} }
fn goals_data<'a>(self, goals: &'a Self::InternedGoals) -> &'a [Goal<Interner>] { fn goals_data(self, goals: &Self::InternedGoals) -> &[Goal<Interner>] {
goals goals
} }
@ -300,10 +297,7 @@ impl chalk_ir::interner::Interner for Interner {
Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
} }
fn substitution_data<'a>( fn substitution_data(self, substitution: &Self::InternedSubstitution) -> &[GenericArg] {
self,
substitution: &'a Self::InternedSubstitution,
) -> &'a [GenericArg] {
&substitution.as_ref().0 &substitution.as_ref().0
} }
@ -314,10 +308,10 @@ impl chalk_ir::interner::Interner for Interner {
data data
} }
fn program_clause_data<'a>( fn program_clause_data(
self, self,
clause: &'a Self::InternedProgramClause, clause: &Self::InternedProgramClause,
) -> &'a chalk_ir::ProgramClauseData<Self> { ) -> &chalk_ir::ProgramClauseData<Self> {
clause clause
} }
@ -328,10 +322,10 @@ impl chalk_ir::interner::Interner for Interner {
Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
} }
fn program_clauses_data<'a>( fn program_clauses_data(
self, self,
clauses: &'a Self::InternedProgramClauses, clauses: &Self::InternedProgramClauses,
) -> &'a [chalk_ir::ProgramClause<Self>] { ) -> &[chalk_ir::ProgramClause<Self>] {
clauses clauses
} }
@ -342,10 +336,10 @@ impl chalk_ir::interner::Interner for Interner {
Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
} }
fn quantified_where_clauses_data<'a>( fn quantified_where_clauses_data(
self, self,
clauses: &'a Self::InternedQuantifiedWhereClauses, clauses: &Self::InternedQuantifiedWhereClauses,
) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] { ) -> &[chalk_ir::QuantifiedWhereClause<Self>] {
clauses clauses
} }
@ -356,10 +350,10 @@ impl chalk_ir::interner::Interner for Interner {
Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
} }
fn variable_kinds_data<'a>( fn variable_kinds_data(
self, self,
parameter_kinds: &'a Self::InternedVariableKinds, parameter_kinds: &Self::InternedVariableKinds,
) -> &'a [chalk_ir::VariableKind<Self>] { ) -> &[chalk_ir::VariableKind<Self>] {
&parameter_kinds.as_ref().0 &parameter_kinds.as_ref().0
} }
@ -370,10 +364,10 @@ impl chalk_ir::interner::Interner for Interner {
Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
} }
fn canonical_var_kinds_data<'a>( fn canonical_var_kinds_data(
self, self,
canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, canonical_var_kinds: &Self::InternedCanonicalVarKinds,
) -> &'a [chalk_ir::CanonicalVarKind<Self>] { ) -> &[chalk_ir::CanonicalVarKind<Self>] {
canonical_var_kinds canonical_var_kinds
} }
@ -384,10 +378,10 @@ impl chalk_ir::interner::Interner for Interner {
data.into_iter().collect() data.into_iter().collect()
} }
fn constraints_data<'a>( fn constraints_data(
self, self,
constraints: &'a Self::InternedConstraints, constraints: &Self::InternedConstraints,
) -> &'a [chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] { ) -> &[chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] {
constraints constraints
} }
fn debug_closure_id( fn debug_closure_id(
@ -410,10 +404,7 @@ impl chalk_ir::interner::Interner for Interner {
Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
} }
fn variances_data<'a>( fn variances_data(self, variances: &Self::InternedVariances) -> &[chalk_ir::Variance] {
self,
variances: &'a Self::InternedVariances,
) -> &'a [chalk_ir::Variance] {
variances variances
} }
} }

View file

@ -0,0 +1,20 @@
//! Functions to detect special lang items
use hir_def::{AdtId, HasModule};
use hir_expand::name;
use crate::db::HirDatabase;
pub fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
let owned_box = name![owned_box].to_smol_str();
let krate = adt.module(db.upcast()).krate();
let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
Some(adt) == box_adt
}
pub fn is_unsafe_cell(adt: AdtId, db: &dyn HirDatabase) -> bool {
let owned_box = name![unsafe_cell].to_smol_str();
let krate = adt.module(db.upcast()).krate();
let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
Some(adt) == box_adt
}

279
crates/hir-ty/src/layout.rs Normal file
View file

@ -0,0 +1,279 @@
//! Compute the binary representation of a type
use std::sync::Arc;
use base_db::CrateId;
use chalk_ir::{AdtId, TyKind};
use hir_def::{
layout::{
Abi, FieldsShape, Integer, Layout, LayoutCalculator, LayoutError, Primitive, ReprOptions,
RustcEnumVariantIdx, Scalar, Size, StructKind, TargetDataLayout, Variants, WrappingRange,
},
LocalFieldId,
};
use stdx::never;
use crate::{db::HirDatabase, Interner, Substitution, Ty};
use self::adt::struct_variant_idx;
pub use self::{
adt::{layout_of_adt_query, layout_of_adt_recover},
target::target_data_layout_query,
};
macro_rules! user_error {
($x: expr) => {
return Err(LayoutError::UserError(format!($x)))
};
}
mod adt;
mod target;
struct LayoutCx<'a> {
db: &'a dyn HirDatabase,
krate: CrateId,
}
impl LayoutCalculator for LayoutCx<'_> {
type TargetDataLayoutRef = Arc<TargetDataLayout>;
fn delay_bug(&self, txt: &str) {
never!("{}", txt);
}
fn current_data_layout(&self) -> Arc<TargetDataLayout> {
self.db.target_data_layout(self.krate)
}
}
fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
}
fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout {
Layout::scalar(dl, scalar_unit(dl, value))
}
pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Layout, LayoutError> {
let cx = LayoutCx { db, krate };
let dl = &*cx.current_data_layout();
Ok(match ty.kind(Interner) {
TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
TyKind::Scalar(s) => match s {
chalk_ir::Scalar::Bool => Layout::scalar(
dl,
Scalar::Initialized {
value: Primitive::Int(Integer::I8, false),
valid_range: WrappingRange { start: 0, end: 1 },
},
),
chalk_ir::Scalar::Char => Layout::scalar(
dl,
Scalar::Initialized {
value: Primitive::Int(Integer::I32, false),
valid_range: WrappingRange { start: 0, end: 0x10FFFF },
},
),
chalk_ir::Scalar::Int(i) => scalar(
dl,
Primitive::Int(
match i {
chalk_ir::IntTy::Isize => dl.ptr_sized_integer(),
chalk_ir::IntTy::I8 => Integer::I8,
chalk_ir::IntTy::I16 => Integer::I16,
chalk_ir::IntTy::I32 => Integer::I32,
chalk_ir::IntTy::I64 => Integer::I64,
chalk_ir::IntTy::I128 => Integer::I128,
},
true,
),
),
chalk_ir::Scalar::Uint(i) => scalar(
dl,
Primitive::Int(
match i {
chalk_ir::UintTy::Usize => dl.ptr_sized_integer(),
chalk_ir::UintTy::U8 => Integer::I8,
chalk_ir::UintTy::U16 => Integer::I16,
chalk_ir::UintTy::U32 => Integer::I32,
chalk_ir::UintTy::U64 => Integer::I64,
chalk_ir::UintTy::U128 => Integer::I128,
},
false,
),
),
chalk_ir::Scalar::Float(f) => scalar(
dl,
match f {
chalk_ir::FloatTy::F32 => Primitive::F32,
chalk_ir::FloatTy::F64 => Primitive::F64,
},
),
},
TyKind::Tuple(len, tys) => {
let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
let fields = tys
.iter(Interner)
.map(|k| layout_of_ty(db, k.assert_ty_ref(Interner), krate))
.collect::<Result<Vec<_>, _>>()?;
let fields = fields.iter().collect::<Vec<_>>();
let fields = fields.iter().collect::<Vec<_>>();
cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
}
TyKind::Array(element, count) => {
let count = match count.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match c.interned {
hir_def::type_ref::ConstScalar::Int(x) => x as u64,
hir_def::type_ref::ConstScalar::UInt(x) => x as u64,
hir_def::type_ref::ConstScalar::Unknown => {
user_error!("unknown const generic parameter")
}
_ => user_error!("mismatched type of const generic parameter"),
},
_ => return Err(LayoutError::HasPlaceholder),
};
let element = layout_of_ty(db, element, krate)?;
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) {
Abi::Uninhabited
} else {
Abi::Aggregate { sized: true }
};
let largest_niche = if count != 0 { element.largest_niche } else { None };
Layout {
variants: Variants::Single { index: struct_variant_idx() },
fields: FieldsShape::Array { stride: element.size, count },
abi,
largest_niche,
align: element.align,
size,
}
}
TyKind::Slice(element) => {
let element = layout_of_ty(db, element, krate)?;
Layout {
variants: Variants::Single { index: struct_variant_idx() },
fields: FieldsShape::Array { stride: element.size, count: 0 },
abi: Abi::Aggregate { sized: false },
largest_niche: None,
align: element.align,
size: Size::ZERO,
}
}
// Potentially-wide pointers.
TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => {
let mut data_ptr = scalar_unit(dl, Primitive::Pointer);
if matches!(ty.kind(Interner), TyKind::Ref(..)) {
data_ptr.valid_range_mut().start = 1;
}
// let pointee = tcx.normalize_erasing_regions(param_env, pointee);
// if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
// return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr)));
// }
let unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone());
let metadata = match unsized_part.kind(Interner) {
TyKind::Slice(_) | TyKind::Str => {
scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
}
TyKind::Dyn(..) => {
let mut vtable = scalar_unit(dl, Primitive::Pointer);
vtable.valid_range_mut().start = 1;
vtable
}
_ => {
// pointee is sized
return Ok(Layout::scalar(dl, data_ptr));
}
};
// Effectively a (ptr, meta) tuple.
cx.scalar_pair(data_ptr, metadata)
}
TyKind::FnDef(_, _) => layout_of_unit(&cx, dl)?,
TyKind::Str => Layout {
variants: Variants::Single { index: struct_variant_idx() },
fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 },
abi: Abi::Aggregate { sized: false },
largest_niche: None,
align: dl.i8_align,
size: Size::ZERO,
},
TyKind::Never => Layout {
variants: Variants::Single { index: struct_variant_idx() },
fields: FieldsShape::Primitive,
abi: Abi::Uninhabited,
largest_niche: None,
align: dl.i8_align,
size: Size::ZERO,
},
TyKind::Dyn(_) | TyKind::Foreign(_) => {
let mut unit = layout_of_unit(&cx, dl)?;
match unit.abi {
Abi::Aggregate { ref mut sized } => *sized = false,
_ => user_error!("bug"),
}
unit
}
TyKind::Function(_) => {
let mut ptr = scalar_unit(dl, Primitive::Pointer);
ptr.valid_range_mut().start = 1;
Layout::scalar(dl, ptr)
}
TyKind::Closure(_, _)
| TyKind::OpaqueType(_, _)
| TyKind::Generator(_, _)
| TyKind::GeneratorWitness(_, _) => return Err(LayoutError::NotImplemented),
TyKind::AssociatedType(_, _)
| TyKind::Error
| TyKind::Alias(_)
| TyKind::Placeholder(_)
| TyKind::BoundVar(_)
| TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder),
})
}
fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> {
cx.univariant::<RustcEnumVariantIdx, &&Layout>(
dl,
&[],
&ReprOptions::default(),
StructKind::AlwaysSized,
)
.ok_or(LayoutError::Unknown)
}
fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty {
match pointee.kind(Interner) {
TyKind::Adt(AdtId(adt), subst) => match adt {
&hir_def::AdtId::StructId(i) => {
let data = db.struct_data(i);
let mut it = data.variant_data.fields().iter().rev();
match it.next() {
Some((f, _)) => field_ty(db, i.into(), f, subst),
None => pointee,
}
}
_ => pointee,
},
_ => pointee,
}
}
fn field_ty(
db: &dyn HirDatabase,
def: hir_def::VariantId,
fd: LocalFieldId,
subst: &Substitution,
) -> Ty {
db.field_types(def)[fd].clone().substitute(Interner, subst)
}
#[cfg(test)]
mod tests;

View file

@ -0,0 +1,134 @@
//! Compute the binary representation of structs, unions and enums
use std::ops::Bound;
use hir_def::{
adt::VariantData,
layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
};
use la_arena::RawIdx;
use smallvec::SmallVec;
use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution};
use super::{layout_of_ty, LayoutCx};
pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
}
pub fn layout_of_adt_query(
db: &dyn HirDatabase,
def: AdtId,
subst: Substitution,
) -> Result<Layout, LayoutError> {
let cx = LayoutCx { db, krate: def.module(db.upcast()).krate() };
let dl = cx.current_data_layout();
let handle_variant = |def: VariantId, var: &VariantData| {
var.fields()
.iter()
.map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate))
.collect::<Result<Vec<_>, _>>()
};
let (variants, is_enum, is_union, repr) = match def {
AdtId::StructId(s) => {
let data = db.struct_data(s);
let mut r = SmallVec::<[_; 1]>::new();
r.push(handle_variant(s.into(), &data.variant_data)?);
(r, false, false, data.repr.unwrap_or_default())
}
AdtId::UnionId(id) => {
let data = db.union_data(id);
let mut r = SmallVec::new();
r.push(handle_variant(id.into(), &data.variant_data)?);
(r, false, true, data.repr.unwrap_or_default())
}
AdtId::EnumId(e) => {
let data = db.enum_data(e);
let r = data
.variants
.iter()
.map(|(idx, v)| {
handle_variant(
EnumVariantId { parent: e, local_id: idx }.into(),
&v.variant_data,
)
})
.collect::<Result<SmallVec<_>, _>>()?;
(r, true, false, data.repr.unwrap_or_default())
}
};
let variants =
variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<SmallVec<[_; 1]>>();
let variants = variants.iter().map(|x| x.iter().collect()).collect();
if is_union {
cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
} else {
cx.layout_of_struct_or_enum(
&repr,
&variants,
is_enum,
is_unsafe_cell(def, db),
layout_scalar_valid_range(db, def),
|min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
variants.iter_enumerated().filter_map(|(id, _)| {
let AdtId::EnumId(e) = def else { return None };
let d = match db
.const_eval_variant(EnumVariantId { parent: e, local_id: id.0 })
.ok()?
{
crate::consteval::ComputedExpr::Literal(l) => match l {
hir_def::expr::Literal::Int(i, _) => i,
hir_def::expr::Literal::Uint(i, _) => i as i128,
_ => return None,
},
_ => return None,
};
Some((id, d))
}),
// FIXME: The current code for niche-filling relies on variant indices
// instead of actual discriminants, so enums with
// explicit discriminants (RFC #2363) would misbehave and we should disable
// niche optimization for them.
// The code that do it in rustc:
// repr.inhibit_enum_layout_opt() || def
// .variants()
// .iter_enumerated()
// .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
repr.inhibit_enum_layout_opt(),
!is_enum
&& variants
.iter()
.next()
.and_then(|x| x.last().map(|x| x.is_unsized()))
.unwrap_or(true),
)
.ok_or(LayoutError::SizeOverflow)
}
}
fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
let attrs = db.attrs(def.into());
let get = |name| {
let attr = attrs.by_key(name).tt_values();
for tree in attr {
if let Some(x) = tree.token_trees.first() {
if let Ok(x) = x.to_string().parse() {
return Bound::Included(x);
}
}
}
Bound::Unbounded
};
(get("rustc_layout_scalar_valid_range_start"), get("rustc_layout_scalar_valid_range_end"))
}
pub fn layout_of_adt_recover(
_: &dyn HirDatabase,
_: &[String],
_: &AdtId,
_: &Substitution,
) -> Result<Layout, LayoutError> {
user_error!("infinite sized recursive type");
}

View file

@ -0,0 +1,36 @@
//! Target dependent parameters needed for layouts
use std::sync::Arc;
use base_db::CrateId;
use hir_def::layout::{Endian, Size, TargetDataLayout};
use crate::db::HirDatabase;
pub fn target_data_layout_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<TargetDataLayout> {
let crate_graph = db.crate_graph();
let target_layout = &crate_graph[krate].target_layout;
let cfg_options = &crate_graph[krate].cfg_options;
Arc::new(
target_layout
.as_ref()
.and_then(|it| TargetDataLayout::parse_from_llvm_datalayout_string(it).ok())
.unwrap_or_else(|| {
let endian = match cfg_options.get_cfg_values("target_endian").next() {
Some(x) if x.as_str() == "big" => Endian::Big,
_ => Endian::Little,
};
let pointer_size = Size::from_bytes(
match cfg_options.get_cfg_values("target_pointer_width").next() {
Some(x) => match x.as_str() {
"16" => 2,
"32" => 4,
_ => 8,
},
_ => 8,
},
);
TargetDataLayout { endian, pointer_size, ..TargetDataLayout::default() }
}),
)
}

View file

@ -0,0 +1,208 @@
use base_db::fixture::WithFixture;
use chalk_ir::{AdtId, TyKind};
use hir_def::{
db::DefDatabase,
layout::{Layout, LayoutError},
};
use crate::{test_db::TestDB, Interner, Substitution};
use super::layout_of_ty;
fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
// using unstable cargo features failed, fall back to using plain rustc
let mut cmd = std::process::Command::new("rustc");
cmd.args(["-Z", "unstable-options", "--print", "target-spec-json"]).env("RUSTC_BOOTSTRAP", "1");
let output = cmd.output().unwrap();
assert!(output.status.success(), "{}", output.status);
let stdout = String::from_utf8(output.stdout).unwrap();
let target_data_layout =
stdout.split_once(r#""data-layout": ""#).unwrap().1.split_once('"').unwrap().0.to_owned();
let ra_fixture = format!(
"{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}",
);
let (db, file_id) = TestDB::with_single_file(&ra_fixture);
let module_id = db.module_for_file(file_id);
let def_map = module_id.def_map(&db);
let scope = &def_map[module_id.local_id].scope;
let adt_id = scope
.declarations()
.find_map(|x| match x {
hir_def::ModuleDefId::AdtId(x) => {
let name = match x {
hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(),
hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(),
hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(),
};
(name == "Goal").then_some(x)
}
_ => None,
})
.unwrap();
let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
layout_of_ty(&db, &goal_ty, module_id.krate())
}
#[track_caller]
fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
let l = eval_goal(ra_fixture, minicore).unwrap();
assert_eq!(l.size.bytes(), size);
assert_eq!(l.align.abi.bytes(), align);
}
#[track_caller]
fn check_fail(ra_fixture: &str, e: LayoutError) {
let r = eval_goal(ra_fixture, "");
assert_eq!(r, Err(e));
}
macro_rules! size_and_align {
(minicore: $($x:tt),*;$($t:tt)*) => {
{
#[allow(dead_code)]
$($t)*
check_size_and_align(
stringify!($($t)*),
&format!("//- minicore: {}\n", stringify!($($x),*)),
::std::mem::size_of::<Goal>() as u64,
::std::mem::align_of::<Goal>() as u64,
);
}
};
($($t:tt)*) => {
{
#[allow(dead_code)]
$($t)*
check_size_and_align(
stringify!($($t)*),
"",
::std::mem::size_of::<Goal>() as u64,
::std::mem::align_of::<Goal>() as u64,
);
}
};
}
#[test]
fn hello_world() {
size_and_align! {
struct Goal(i32);
}
}
#[test]
fn field_order_optimization() {
size_and_align! {
struct Goal(u8, i32, u8);
}
size_and_align! {
#[repr(C)]
struct Goal(u8, i32, u8);
}
}
#[test]
fn recursive() {
size_and_align! {
struct Goal {
left: &'static Goal,
right: &'static Goal,
}
}
size_and_align! {
struct BoxLike<T: ?Sized>(*mut T);
struct Goal(BoxLike<Goal>);
}
check_fail(
r#"struct Goal(Goal);"#,
LayoutError::UserError("infinite sized recursive type".to_string()),
);
check_fail(
r#"
struct Foo<T>(Foo<T>);
struct Goal(Foo<i32>);
"#,
LayoutError::UserError("infinite sized recursive type".to_string()),
);
}
#[test]
fn generic() {
size_and_align! {
struct Pair<A, B>(A, B);
struct Goal(Pair<Pair<i32, u8>, i64>);
}
size_and_align! {
struct X<const N: usize> {
field1: [i32; N],
field2: [u8; N],
}
struct Goal(X<1000>);
}
}
#[test]
fn enums() {
size_and_align! {
enum Goal {
Quit,
Move { x: i32, y: i32 },
ChangeColor(i32, i32, i32),
}
}
}
#[test]
fn primitives() {
size_and_align! {
struct Goal(i32, i128, isize, usize, f32, f64, bool, char);
}
}
#[test]
fn tuple() {
size_and_align! {
struct Goal((), (i32, u64, bool));
}
}
#[test]
fn non_zero() {
size_and_align! {
minicore: non_zero, option;
use core::num::NonZeroU8;
struct Goal(Option<NonZeroU8>);
}
}
#[test]
fn niche_optimization() {
size_and_align! {
minicore: option;
struct Goal(Option<&'static i32>);
}
size_and_align! {
minicore: option;
struct Goal(Option<Option<bool>>);
}
}
#[test]
fn enums_with_discriminants() {
size_and_align! {
enum Goal {
A = 1000,
B = 2000,
C = 3000,
}
}
size_and_align! {
enum Goal {
A = 254,
B,
C, // implicitly becomes 256, so we need two bytes
}
}
}

View file

@ -27,6 +27,8 @@ pub mod display;
pub mod method_resolution; pub mod method_resolution;
pub mod primitive; pub mod primitive;
pub mod traits; pub mod traits;
pub mod layout;
pub mod lang_items;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -38,7 +40,7 @@ use std::sync::Arc;
use chalk_ir::{ use chalk_ir::{
fold::{Shift, TypeFoldable}, fold::{Shift, TypeFoldable},
interner::HasInterner, interner::HasInterner,
NoSolution, UniverseIndex, NoSolution,
}; };
use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId}; use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
use hir_expand::name; use hir_expand::name;
@ -46,7 +48,9 @@ use itertools::Either;
use traits::FnTrait; use traits::FnTrait;
use utils::Generics; use utils::Generics;
use crate::{consteval::unknown_const, db::HirDatabase, utils::generics}; use crate::{
consteval::unknown_const, db::HirDatabase, infer::unify::InferenceTable, utils::generics,
};
pub use autoderef::autoderef; pub use autoderef::autoderef;
pub use builder::{ParamKind, TyBuilder}; pub use builder::{ParamKind, TyBuilder};
@ -511,7 +515,7 @@ where
let mut error_replacer = ErrorReplacer { vars: 0 }; let mut error_replacer = ErrorReplacer { vars: 0 };
let value = match t.clone().try_fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) { let value = match t.clone().try_fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) {
Ok(t) => t, Ok(t) => t,
Err(_) => panic!("Encountered unbound or inference vars in {:?}", t), Err(_) => panic!("Encountered unbound or inference vars in {t:?}"),
}; };
let kinds = (0..error_replacer.vars).map(|_| { let kinds = (0..error_replacer.vars).map(|_| {
chalk_ir::CanonicalVarKind::new( chalk_ir::CanonicalVarKind::new(
@ -531,54 +535,31 @@ pub fn callable_sig_from_fnonce(
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
let mut table = InferenceTable::new(db, env.clone());
let b = TyBuilder::trait_ref(db, fn_once_trait); let b = TyBuilder::trait_ref(db, fn_once_trait);
if b.remaining() != 2 { if b.remaining() != 2 {
return None; return None;
} }
let fn_once = b.push(self_ty.clone()).fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build();
let kinds = fn_once
.substitution
.iter(Interner)
.skip(1)
.map(|x| {
let vk = match x.data(Interner) {
chalk_ir::GenericArgData::Ty(_) => {
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
}
chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
chalk_ir::GenericArgData::Const(c) => {
chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
}
};
chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
})
.collect::<Vec<_>>();
// FIXME: chalk refuses to solve `<Self as FnOnce<^0.0>>::Output == ^0.1`, so we first solve // Register two obligations:
// `<Self as FnOnce<^0.0>>` and then replace `^0.0` with the concrete argument tuple. // - Self: FnOnce<?args_ty>
let trait_env = env.env.clone(); // - <Self as FnOnce<?args_ty>>::Output == ?ret_ty
let obligation = InEnvironment { goal: fn_once.cast(Interner), environment: trait_env }; let args_ty = table.new_type_var();
let canonical = let trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build();
Canonical { binders: CanonicalVarKinds::from_iter(Interner, kinds), value: obligation }; let projection = TyBuilder::assoc_type_projection(
let subst = match db.trait_solve(krate, canonical) { db,
Some(Solution::Unique(vars)) => vars.value.subst, output_assoc_type,
_ => return None, Some(trait_ref.substitution.clone()),
}; )
let args = subst.at(Interner, 0).ty(Interner)?; .build();
let params = match args.kind(Interner) { table.register_obligation(trait_ref.cast(Interner));
chalk_ir::TyKind::Tuple(_, subst) => { let ret_ty = table.normalize_projection_ty(projection);
subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::<Vec<_>>()
}
_ => return None,
};
let fn_once = let ret_ty = table.resolve_completely(ret_ty);
TyBuilder::trait_ref(db, fn_once_trait).push(self_ty.clone()).push(args.clone()).build(); let args_ty = table.resolve_completely(args_ty);
let projection =
TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone()))
.build();
let ret_ty = db.normalize_projection(projection, env); let params =
args_ty.as_tuple()?.iter(Interner).map(|it| it.assert_ty_ref(Interner)).cloned().collect();
Some(CallableSig::from_params_and_return(params, ret_ty.clone(), false, Safety::Safe)) Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe))
} }

View file

@ -603,9 +603,8 @@ impl<'a> TyLoweringContext<'a> {
} }
fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty { fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
let (def, res) = match (self.resolver.generic_def(), res) { let Some((def, res)) = self.resolver.generic_def().zip(res) else {
(Some(def), Some(res)) => (def, res), return TyKind::Error.intern(Interner);
_ => return TyKind::Error.intern(Interner),
}; };
let ty = named_associated_type_shorthand_candidates( let ty = named_associated_type_shorthand_candidates(
self.db, self.db,
@ -617,6 +616,21 @@ impl<'a> TyLoweringContext<'a> {
return None; return None;
} }
let parent_subst = t.substitution.clone();
let parent_subst = match self.type_param_mode {
ParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put them in now.
let generics = generics(self.db.upcast(), def);
let s = generics.placeholder_subst(self.db);
s.apply(parent_subst, Interner)
}
ParamLoweringMode::Variable => {
// We need to shift in the bound vars, since
// `named_associated_type_shorthand_candidates` does not do that.
parent_subst.shifted_in_from(Interner, self.in_binders)
}
};
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want // generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at // that method to optionally take parent `Substitution` as we already know them at
@ -632,22 +646,9 @@ impl<'a> TyLoweringContext<'a> {
let substs = Substitution::from_iter( let substs = Substitution::from_iter(
Interner, Interner,
substs.iter(Interner).take(len_self).chain(t.substitution.iter(Interner)), substs.iter(Interner).take(len_self).chain(parent_subst.iter(Interner)),
); );
let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put
// them in now
let generics = generics(self.db.upcast(), def);
let s = generics.placeholder_subst(self.db);
s.apply(substs, Interner)
}
ParamLoweringMode::Variable => substs,
};
// We need to shift in the bound vars, since
// associated_type_shorthand_candidates does not do that
let substs = substs.shifted_in_from(Interner, self.in_binders);
Some( Some(
TyKind::Alias(AliasTy::Projection(ProjectionTy { TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty), associated_ty_id: to_assoc_type_id(associated_ty),
@ -779,7 +780,7 @@ impl<'a> TyLoweringContext<'a> {
|_, c, ty| { |_, c, ty| {
const_or_path_to_chalk( const_or_path_to_chalk(
self.db, self.db,
&self.resolver, self.resolver,
ty, ty,
c, c,
self.type_param_mode, self.type_param_mode,
@ -1190,9 +1191,9 @@ pub fn associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase, db: &dyn HirDatabase,
def: GenericDefId, def: GenericDefId,
res: TypeNs, res: TypeNs,
cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, mut cb: impl FnMut(&Name, TypeAliasId) -> Option<R>,
) -> Option<R> { ) -> Option<R> {
named_associated_type_shorthand_candidates(db, def, res, None, cb) named_associated_type_shorthand_candidates(db, def, res, None, |name, _, id| cb(name, id))
} }
fn named_associated_type_shorthand_candidates<R>( fn named_associated_type_shorthand_candidates<R>(
@ -1202,6 +1203,9 @@ fn named_associated_type_shorthand_candidates<R>(
def: GenericDefId, def: GenericDefId,
res: TypeNs, res: TypeNs,
assoc_name: Option<Name>, assoc_name: Option<Name>,
// Do NOT let `cb` touch `TraitRef` outside of `TyLoweringContext`. Its substitution contains
// free `BoundVar`s that need to be shifted and only `TyLoweringContext` knows how to do that
// properly (see `TyLoweringContext::select_associated_type()`).
mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
) -> Option<R> { ) -> Option<R> {
let mut search = |t| { let mut search = |t| {
@ -1792,8 +1796,7 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
let impl_data = db.impl_data(impl_id); let impl_data = db.impl_data(impl_id);
let resolver = impl_id.resolver(db.upcast()); let resolver = impl_id.resolver(db.upcast());
let _cx = stdx::panic_context::enter(format!( let _cx = stdx::panic_context::enter(format!(
"impl_self_ty_query({:?} -> {:?} -> {:?})", "impl_self_ty_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})"
impl_id, impl_loc, impl_data
)); ));
let generics = generics(db.upcast(), impl_id.into()); let generics = generics(db.upcast(), impl_id.into());
let ctx = let ctx =
@ -1830,8 +1833,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
let impl_data = db.impl_data(impl_id); let impl_data = db.impl_data(impl_id);
let resolver = impl_id.resolver(db.upcast()); let resolver = impl_id.resolver(db.upcast());
let _cx = stdx::panic_context::enter(format!( let _cx = stdx::panic_context::enter(format!(
"impl_trait_query({:?} -> {:?} -> {:?})", "impl_trait_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})"
impl_id, impl_loc, impl_data
)); ));
let ctx = let ctx =
TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable); TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
@ -1850,7 +1852,7 @@ pub(crate) fn return_type_impl_traits(
let ctx_ret = TyLoweringContext::new(db, &resolver) let ctx_ret = TyLoweringContext::new(db, &resolver)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable); .with_type_param_mode(ParamLoweringMode::Variable);
let _ret = (&ctx_ret).lower_ty(&data.ret_type); let _ret = ctx_ret.lower_ty(&data.ret_type);
let generics = generics(db.upcast(), def.into()); let generics = generics(db.upcast(), def.into());
let return_type_impl_traits = let return_type_impl_traits =
ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() }; ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() };
@ -1979,7 +1981,7 @@ fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Intern
if bound.index_if_innermost().map_or(true, is_allowed) { if bound.index_if_innermost().map_or(true, is_allowed) {
bound.shifted_in_from(binders).to_const(Interner, ty) bound.shifted_in_from(binders).to_const(Interner, ty)
} else { } else {
unknown_const(ty.clone()) unknown_const(ty)
} }
}, },
) )

View file

@ -2,18 +2,17 @@
//! For details about how this works in rustc, see the method lookup page in the //! For details about how this works in rustc, see the method lookup page in the
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
use std::{iter, ops::ControlFlow, sync::Arc}; use std::{ops::ControlFlow, sync::Arc};
use arrayvec::ArrayVec;
use base_db::{CrateId, Edition}; use base_db::{CrateId, Edition};
use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
use hir_def::{ use hir_def::{
data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId,
FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
TraitId,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
use stdx::never; use stdx::never;
use crate::{ use crate::{
@ -336,21 +335,18 @@ impl InherentImpls {
} }
} }
pub(crate) fn inherent_impl_crates_query( pub(crate) fn incoherent_inherent_impl_crates(
db: &dyn HirDatabase, db: &dyn HirDatabase,
krate: CrateId, krate: CrateId,
fp: TyFingerprint, fp: TyFingerprint,
) -> ArrayVec<CrateId, 2> { ) -> SmallVec<[CrateId; 2]> {
let _p = profile::span("inherent_impl_crates_query"); let _p = profile::span("inherent_impl_crates_query");
let mut res = ArrayVec::new(); let mut res = SmallVec::new();
let crate_graph = db.crate_graph(); let crate_graph = db.crate_graph();
// should pass crate for finger print and do reverse deps
for krate in crate_graph.transitive_deps(krate) { for krate in crate_graph.transitive_deps(krate) {
if res.is_full() {
// we don't currently look for or store more than two crates here,
// so don't needlessly look at more crates than necessary.
break;
}
let impls = db.inherent_impls_in_crate(krate); let impls = db.inherent_impls_in_crate(krate);
if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) { if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) {
res.push(krate); res.push(krate);
@ -392,19 +388,40 @@ pub fn def_crates(
db: &dyn HirDatabase, db: &dyn HirDatabase,
ty: &Ty, ty: &Ty,
cur_crate: CrateId, cur_crate: CrateId,
) -> Option<ArrayVec<CrateId, 2>> { ) -> Option<SmallVec<[CrateId; 2]>> {
let mod_to_crate_ids = |module: ModuleId| Some(iter::once(module.krate()).collect());
let fp = TyFingerprint::for_inherent_impl(ty);
match ty.kind(Interner) { match ty.kind(Interner) {
TyKind::Adt(AdtId(def_id), _) => mod_to_crate_ids(def_id.module(db.upcast())), &TyKind::Adt(AdtId(def_id), _) => {
TyKind::Foreign(id) => { let rustc_has_incoherent_inherent_impls = match def_id {
mod_to_crate_ids(from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast())) hir_def::AdtId::StructId(id) => {
db.struct_data(id).rustc_has_incoherent_inherent_impls
}
hir_def::AdtId::UnionId(id) => {
db.union_data(id).rustc_has_incoherent_inherent_impls
}
hir_def::AdtId::EnumId(id) => db.enum_data(id).rustc_has_incoherent_inherent_impls,
};
Some(if rustc_has_incoherent_inherent_impls {
db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Adt(def_id))
} else {
smallvec![def_id.module(db.upcast()).krate()]
})
}
&TyKind::Foreign(id) => {
let alias = from_foreign_def_id(id);
Some(if db.type_alias_data(alias).rustc_has_incoherent_inherent_impls {
db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::ForeignType(id))
} else {
smallvec![alias.module(db.upcast()).krate()]
})
}
TyKind::Dyn(_) => {
let trait_id = ty.dyn_trait()?;
Some(if db.trait_data(trait_id).rustc_has_incoherent_inherent_impls {
db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id))
} else {
smallvec![trait_id.module(db.upcast()).krate()]
})
} }
TyKind::Dyn(_) => ty
.dyn_trait()
.and_then(|trait_| mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))),
// for primitives, there may be impls in various places (core and alloc // for primitives, there may be impls in various places (core and alloc
// mostly). We just check the whole crate graph for crates with impls // mostly). We just check the whole crate graph for crates with impls
// (cached behind a query). // (cached behind a query).
@ -412,10 +429,11 @@ pub fn def_crates(
| TyKind::Str | TyKind::Str
| TyKind::Slice(_) | TyKind::Slice(_)
| TyKind::Array(..) | TyKind::Array(..)
| TyKind::Raw(..) => { | TyKind::Raw(..) => Some(db.incoherent_inherent_impl_crates(
Some(db.inherent_impl_crates(cur_crate, fp.expect("fingerprint for primitive"))) cur_crate,
} TyFingerprint::for_inherent_impl(ty).expect("fingerprint for primitive"),
_ => return None, )),
_ => None,
} }
} }
@ -470,14 +488,15 @@ pub fn lang_names_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, Name)>
/// Look up the method with the given name. /// Look up the method with the given name.
pub(crate) fn lookup_method( pub(crate) fn lookup_method(
ty: &Canonical<Ty>,
db: &dyn HirDatabase, db: &dyn HirDatabase,
ty: &Canonical<Ty>,
env: Arc<TraitEnvironment>, env: Arc<TraitEnvironment>,
traits_in_scope: &FxHashSet<TraitId>, traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: &Name, name: &Name,
) -> Option<(ReceiverAdjustments, FunctionId)> { ) -> Option<(ReceiverAdjustments, FunctionId, bool)> {
iterate_method_candidates( let mut not_visible = None;
let res = iterate_method_candidates(
ty, ty,
db, db,
env, env,
@ -485,11 +504,16 @@ pub(crate) fn lookup_method(
visible_from_module, visible_from_module,
Some(name), Some(name),
LookupMode::MethodCall, LookupMode::MethodCall,
|adjustments, f| match f { |adjustments, f, visible| match f {
AssocItemId::FunctionId(f) => Some((adjustments, f)), AssocItemId::FunctionId(f) if visible => Some((adjustments, f, true)),
AssocItemId::FunctionId(f) if not_visible.is_none() => {
not_visible = Some((adjustments, f, false));
None
}
_ => None, _ => None,
}, },
) );
res.or(not_visible)
} }
/// Whether we're looking up a dotted method call (like `v.len()`) or a path /// Whether we're looking up a dotted method call (like `v.len()`) or a path
@ -601,7 +625,7 @@ pub(crate) fn iterate_method_candidates<T>(
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mode: LookupMode, mode: LookupMode,
mut callback: impl FnMut(ReceiverAdjustments, AssocItemId) -> Option<T>, mut callback: impl FnMut(ReceiverAdjustments, AssocItemId, bool) -> Option<T>,
) -> Option<T> { ) -> Option<T> {
let mut slot = None; let mut slot = None;
iterate_method_candidates_dyn( iterate_method_candidates_dyn(
@ -612,9 +636,9 @@ pub(crate) fn iterate_method_candidates<T>(
visible_from_module, visible_from_module,
name, name,
mode, mode,
&mut |adj, item| { &mut |adj, item, visible| {
assert!(slot.is_none()); assert!(slot.is_none());
if let Some(it) = callback(adj, item) { if let Some(it) = callback(adj, item, visible) {
slot = Some(it); slot = Some(it);
return ControlFlow::Break(()); return ControlFlow::Break(());
} }
@ -624,6 +648,30 @@ pub(crate) fn iterate_method_candidates<T>(
slot slot
} }
pub fn lookup_impl_const(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
const_id: ConstId,
subs: Substitution,
) -> ConstId {
let trait_id = match const_id.lookup(db.upcast()).container {
ItemContainerId::TraitId(id) => id,
_ => return const_id,
};
let substitution = Substitution::from_iter(Interner, subs.iter(Interner));
let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution };
let const_data = db.const_data(const_id);
let name = match const_data.name.as_ref() {
Some(name) => name,
None => return const_id,
};
lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
.and_then(|assoc| if let AssocItemId::ConstId(id) = assoc { Some(id) } else { None })
.unwrap_or(const_id)
}
/// Looks up the impl method that actually runs for the trait method `func`. /// Looks up the impl method that actually runs for the trait method `func`.
/// ///
/// Returns `func` if it's not a method defined in a trait or the lookup failed. /// Returns `func` if it's not a method defined in a trait or the lookup failed.
@ -645,15 +693,17 @@ pub fn lookup_impl_method(
}; };
let name = &db.function_data(func).name; let name = &db.function_data(func).name;
lookup_impl_method_for_trait_ref(trait_ref, db, env, name).unwrap_or(func) lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
.and_then(|assoc| if let AssocItemId::FunctionId(id) = assoc { Some(id) } else { None })
.unwrap_or(func)
} }
fn lookup_impl_method_for_trait_ref( fn lookup_impl_assoc_item_for_trait_ref(
trait_ref: TraitRef, trait_ref: TraitRef,
db: &dyn HirDatabase, db: &dyn HirDatabase,
env: Arc<TraitEnvironment>, env: Arc<TraitEnvironment>,
name: &Name, name: &Name,
) -> Option<FunctionId> { ) -> Option<AssocItemId> {
let self_ty = trait_ref.self_type_parameter(Interner); let self_ty = trait_ref.self_type_parameter(Interner);
let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?; let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
let impls = db.trait_impls_in_deps(env.krate); let impls = db.trait_impls_in_deps(env.krate);
@ -663,7 +713,15 @@ fn lookup_impl_method_for_trait_ref(
let impl_data = find_matching_impl(impls, table, trait_ref)?; let impl_data = find_matching_impl(impls, table, trait_ref)?;
impl_data.items.iter().find_map(|it| match it { impl_data.items.iter().find_map(|it| match it {
AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f), AssocItemId::FunctionId(f) => {
(db.function_data(*f).name == *name).then_some(AssocItemId::FunctionId(*f))
}
AssocItemId::ConstId(c) => db
.const_data(*c)
.name
.as_ref()
.map(|n| *n == *name)
.and_then(|result| if result { Some(AssocItemId::ConstId(*c)) } else { None }),
_ => None, _ => None,
}) })
} }
@ -719,7 +777,7 @@ pub fn iterate_path_candidates(
name, name,
LookupMode::Path, LookupMode::Path,
// the adjustments are not relevant for path lookup // the adjustments are not relevant for path lookup
&mut |_, id| callback(id), &mut |_, id, _| callback(id),
) )
} }
@ -731,7 +789,7 @@ pub fn iterate_method_candidates_dyn(
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mode: LookupMode, mode: LookupMode,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
match mode { match mode {
LookupMode::MethodCall => { LookupMode::MethodCall => {
@ -795,7 +853,7 @@ fn iterate_method_candidates_with_autoref(
traits_in_scope: &FxHashSet<TraitId>, traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) { if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) {
// don't try to resolve methods on unknown types // don't try to resolve methods on unknown types
@ -856,7 +914,7 @@ fn iterate_method_candidates_by_receiver(
traits_in_scope: &FxHashSet<TraitId>, traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let mut table = InferenceTable::new(db, env); let mut table = InferenceTable::new(db, env);
let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); let receiver_ty = table.instantiate_canonical(receiver_ty.clone());
@ -868,7 +926,7 @@ fn iterate_method_candidates_by_receiver(
while let Some((self_ty, _)) = autoderef.next() { while let Some((self_ty, _)) = autoderef.next() {
iterate_inherent_methods( iterate_inherent_methods(
&self_ty, &self_ty,
&mut autoderef.table, autoderef.table,
name, name,
Some(&receiver_ty), Some(&receiver_ty),
Some(receiver_adjustments.clone()), Some(receiver_adjustments.clone()),
@ -883,7 +941,7 @@ fn iterate_method_candidates_by_receiver(
while let Some((self_ty, _)) = autoderef.next() { while let Some((self_ty, _)) = autoderef.next() {
iterate_trait_method_candidates( iterate_trait_method_candidates(
&self_ty, &self_ty,
&mut autoderef.table, autoderef.table,
traits_in_scope, traits_in_scope,
name, name,
Some(&receiver_ty), Some(&receiver_ty),
@ -902,7 +960,7 @@ fn iterate_method_candidates_for_self_ty(
traits_in_scope: &FxHashSet<TraitId>, traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let mut table = InferenceTable::new(db, env); let mut table = InferenceTable::new(db, env);
let self_ty = table.instantiate_canonical(self_ty.clone()); let self_ty = table.instantiate_canonical(self_ty.clone());
@ -933,7 +991,7 @@ fn iterate_trait_method_candidates(
name: Option<&Name>, name: Option<&Name>,
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
receiver_adjustments: Option<ReceiverAdjustments>, receiver_adjustments: Option<ReceiverAdjustments>,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let db = table.db; let db = table.db;
let env = table.trait_env.clone(); let env = table.trait_env.clone();
@ -964,9 +1022,11 @@ fn iterate_trait_method_candidates(
for &(_, item) in data.items.iter() { for &(_, item) in data.items.iter() {
// Don't pass a `visible_from_module` down to `is_valid_candidate`, // Don't pass a `visible_from_module` down to `is_valid_candidate`,
// since only inherent methods should be included into visibility checking. // since only inherent methods should be included into visibility checking.
if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { let visible = match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
continue; IsValidCandidate::Yes => true,
} IsValidCandidate::NotVisible => false,
IsValidCandidate::No => continue,
};
if !known_implemented { if !known_implemented {
let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty); let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty);
if db.trait_solve(env.krate, goal.cast(Interner)).is_none() { if db.trait_solve(env.krate, goal.cast(Interner)).is_none() {
@ -974,7 +1034,7 @@ fn iterate_trait_method_candidates(
} }
} }
known_implemented = true; known_implemented = true;
callback(receiver_adjustments.clone().unwrap_or_default(), item)?; callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
} }
} }
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -987,7 +1047,7 @@ fn iterate_inherent_methods(
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
receiver_adjustments: Option<ReceiverAdjustments>, receiver_adjustments: Option<ReceiverAdjustments>,
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let db = table.db; let db = table.db;
let env = table.trait_env.clone(); let env = table.trait_env.clone();
@ -1076,7 +1136,7 @@ fn iterate_inherent_methods(
name: Option<&Name>, name: Option<&Name>,
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
receiver_adjustments: Option<ReceiverAdjustments>, receiver_adjustments: Option<ReceiverAdjustments>,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
traits: impl Iterator<Item = TraitId>, traits: impl Iterator<Item = TraitId>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let db = table.db; let db = table.db;
@ -1084,9 +1144,13 @@ fn iterate_inherent_methods(
let data = db.trait_data(t); let data = db.trait_data(t);
for &(_, item) in data.items.iter() { for &(_, item) in data.items.iter() {
// We don't pass `visible_from_module` as all trait items should be visible. // We don't pass `visible_from_module` as all trait items should be visible.
if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { let visible =
callback(receiver_adjustments.clone().unwrap_or_default(), item)?; match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
} IsValidCandidate::Yes => true,
IsValidCandidate::NotVisible => false,
IsValidCandidate::No => continue,
};
callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
} }
} }
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -1100,17 +1164,25 @@ fn iterate_inherent_methods(
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
receiver_adjustments: Option<ReceiverAdjustments>, receiver_adjustments: Option<ReceiverAdjustments>,
visible_from_module: Option<ModuleId>, visible_from_module: Option<ModuleId>,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let db = table.db; let db = table.db;
let impls_for_self_ty = impls.for_self_ty(self_ty); let impls_for_self_ty = impls.for_self_ty(self_ty);
for &impl_def in impls_for_self_ty { for &impl_def in impls_for_self_ty {
for &item in &db.impl_data(impl_def).items { for &item in &db.impl_data(impl_def).items {
if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module) let visible = match is_valid_candidate(
{ table,
continue; name,
} receiver_ty,
callback(receiver_adjustments.clone().unwrap_or_default(), item)?; item,
self_ty,
visible_from_module,
) {
IsValidCandidate::Yes => true,
IsValidCandidate::NotVisible => false,
IsValidCandidate::No => continue,
};
callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
} }
} }
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -1139,7 +1211,7 @@ pub fn resolve_indexing_op(
macro_rules! check_that { macro_rules! check_that {
($cond:expr) => { ($cond:expr) => {
if !$cond { if !$cond {
return false; return IsValidCandidate::No;
} }
}; };
} }
@ -1151,7 +1223,7 @@ fn is_valid_candidate(
item: AssocItemId, item: AssocItemId,
self_ty: &Ty, self_ty: &Ty,
visible_from_module: Option<ModuleId>, visible_from_module: Option<ModuleId>,
) -> bool { ) -> IsValidCandidate {
let db = table.db; let db = table.db;
match item { match item {
AssocItemId::FunctionId(m) => { AssocItemId::FunctionId(m) => {
@ -1162,31 +1234,37 @@ fn is_valid_candidate(
check_that!(receiver_ty.is_none()); check_that!(receiver_ty.is_none());
check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n))); check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
check_that!(visible_from_module.map_or(true, |from_module| {
let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module); if let Some(from_module) = visible_from_module {
if !v { if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
cov_mark::hit!(const_candidate_not_visible); cov_mark::hit!(const_candidate_not_visible);
return IsValidCandidate::NotVisible;
} }
v }
}));
if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container { if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
let self_ty_matches = table.run_in_snapshot(|table| { let self_ty_matches = table.run_in_snapshot(|table| {
let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id) let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id)
.fill_with_inference_vars(table) .fill_with_inference_vars(table)
.build(); .build();
table.unify(&expected_self_ty, &self_ty) table.unify(&expected_self_ty, self_ty)
}); });
if !self_ty_matches { if !self_ty_matches {
cov_mark::hit!(const_candidate_self_type_mismatch); cov_mark::hit!(const_candidate_self_type_mismatch);
return false; return IsValidCandidate::No;
} }
} }
true IsValidCandidate::Yes
} }
_ => false, _ => IsValidCandidate::No,
} }
} }
enum IsValidCandidate {
Yes,
No,
NotVisible,
}
fn is_valid_fn_candidate( fn is_valid_fn_candidate(
table: &mut InferenceTable<'_>, table: &mut InferenceTable<'_>,
fn_id: FunctionId, fn_id: FunctionId,
@ -1194,19 +1272,17 @@ fn is_valid_fn_candidate(
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
self_ty: &Ty, self_ty: &Ty,
visible_from_module: Option<ModuleId>, visible_from_module: Option<ModuleId>,
) -> bool { ) -> IsValidCandidate {
let db = table.db; let db = table.db;
let data = db.function_data(fn_id); let data = db.function_data(fn_id);
check_that!(name.map_or(true, |n| n == &data.name)); check_that!(name.map_or(true, |n| n == &data.name));
check_that!(visible_from_module.map_or(true, |from_module| { if let Some(from_module) = visible_from_module {
let v = db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module); if !db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module) {
if !v {
cov_mark::hit!(autoderef_candidate_not_visible); cov_mark::hit!(autoderef_candidate_not_visible);
return IsValidCandidate::NotVisible;
} }
v }
}));
table.run_in_snapshot(|table| { table.run_in_snapshot(|table| {
let container = fn_id.lookup(db.upcast()).container; let container = fn_id.lookup(db.upcast()).container;
let (impl_subst, expect_self_ty) = match container { let (impl_subst, expect_self_ty) = match container {
@ -1245,7 +1321,7 @@ fn is_valid_fn_candidate(
// We need to consider the bounds on the impl to distinguish functions of the same name // We need to consider the bounds on the impl to distinguish functions of the same name
// for a type. // for a type.
let predicates = db.generic_predicates(impl_id.into()); let predicates = db.generic_predicates(impl_id.into());
predicates let valid = predicates
.iter() .iter()
.map(|predicate| { .map(|predicate| {
let (p, b) = predicate let (p, b) = predicate
@ -1260,12 +1336,16 @@ fn is_valid_fn_candidate(
// It's ok to get ambiguity here, as we may not have enough information to prove // It's ok to get ambiguity here, as we may not have enough information to prove
// obligations. We'll check if the user is calling the selected method properly // obligations. We'll check if the user is calling the selected method properly
// later anyway. // later anyway.
.all(|p| table.try_obligation(p.cast(Interner)).is_some()) .all(|p| table.try_obligation(p.cast(Interner)).is_some());
match valid {
true => IsValidCandidate::Yes,
false => IsValidCandidate::No,
}
} else { } else {
// For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in
// `iterate_trait_method_candidates()`. // `iterate_trait_method_candidates()`.
// For others, this function shouldn't be called. // For others, this function shouldn't be called.
true IsValidCandidate::Yes
} }
}) })
} }

View file

@ -94,18 +94,19 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
types.insert(file_range, expected.trim_start_matches("type: ").to_string()); types.insert(file_range, expected.trim_start_matches("type: ").to_string());
} else if expected.starts_with("expected") { } else if expected.starts_with("expected") {
mismatches.insert(file_range, expected); mismatches.insert(file_range, expected);
} else if expected.starts_with("adjustments: ") { } else if expected.starts_with("adjustments:") {
adjustments.insert( adjustments.insert(
file_range, file_range,
expected expected
.trim_start_matches("adjustments: ") .trim_start_matches("adjustments:")
.trim()
.split(',') .split(',')
.map(|it| it.trim().to_string()) .map(|it| it.trim().to_string())
.filter(|it| !it.is_empty()) .filter(|it| !it.is_empty())
.collect(), .collect(),
); );
} else { } else {
panic!("unexpected annotation: {}", expected); panic!("unexpected annotation: {expected}");
} }
had_annotations = true; had_annotations = true;
} }
@ -176,17 +177,17 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
if let Some(expected) = adjustments.remove(&range) { if let Some(expected) = adjustments.remove(&range) {
if let Some(adjustments) = inference_result.expr_adjustments.get(&expr) { let adjustments = inference_result
assert_eq!( .expr_adjustments
expected, .get(&expr)
adjustments .map_or_else(Default::default, |it| &**it);
.iter() assert_eq!(
.map(|Adjustment { kind, .. }| format!("{:?}", kind)) expected,
.collect::<Vec<_>>() adjustments
); .iter()
} else { .map(|Adjustment { kind, .. }| format!("{kind:?}"))
panic!("expected {:?} adjustments, found none", expected); .collect::<Vec<_>>()
} );
} }
} }

View file

@ -807,3 +807,37 @@ fn main() {
"#, "#,
); );
} }
#[test]
fn adjust_comparison_arguments() {
check_no_mismatches(
r"
//- minicore: eq
struct Struct;
impl core::cmp::PartialEq for Struct {
fn eq(&self, other: &Self) -> bool { true }
}
fn test() {
Struct == Struct;
// ^^^^^^ adjustments: Borrow(Ref(Not))
// ^^^^^^ adjustments: Borrow(Ref(Not))
}",
);
}
#[test]
fn adjust_assign_lhs() {
check_no_mismatches(
r"
//- minicore: add
struct Struct;
impl core::ops::AddAssign for Struct {
fn add_assign(&mut self, other: Self) {}
}
fn test() {
Struct += Struct;
// ^^^^^^ adjustments: Borrow(Ref(Mut))
// ^^^^^^ adjustments:
}",
);
}

View file

@ -24,7 +24,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
db.infer(def); db.infer(def);
}); });
}); });
assert!(format!("{:?}", events).contains("infer")) assert!(format!("{events:?}").contains("infer"))
} }
let new_text = " let new_text = "
@ -46,6 +46,6 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
db.infer(def); db.infer(def);
}); });
}); });
assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) assert!(!format!("{events:?}").contains("infer"), "{events:#?}")
} }
} }

View file

@ -849,7 +849,7 @@ fn main() {
//^^^^^^^^^^^^^^^^^ RegisterBlock //^^^^^^^^^^^^^^^^^ RegisterBlock
} }
"#; "#;
let fixture = format!("{}\n//- /foo.rs\n{}", fixture, data); let fixture = format!("{fixture}\n//- /foo.rs\n{data}");
{ {
let _b = bench("include macro"); let _b = bench("include macro");

View file

@ -1867,3 +1867,53 @@ fn g<T: Trait>(a: T) {
"#, "#,
); );
} }
#[test]
fn incoherent_impls() {
check(
r#"
//- minicore: error, send
pub struct Box<T>(T);
use core::error::Error;
#[rustc_allow_incoherent_impl]
impl dyn Error {
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
loop {}
}
}
#[rustc_allow_incoherent_impl]
impl dyn Error + Send {
/// Attempts to downcast the box to a concrete type.
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
let err: Box<dyn Error> = self;
// ^^^^ expected Box<dyn Error>, got Box<dyn Error + Send>
// FIXME, type mismatch should not occur
<dyn Error>::downcast(err).map_err(|_| loop {})
//^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error>) -> Result<Box<{unknown}>, Box<dyn Error>>
}
}
"#,
);
}
#[test]
fn fallback_private_methods() {
check(
r#"
mod module {
pub struct Struct;
impl Struct {
fn func(&self) {}
}
}
fn foo() {
let s = module::Struct;
s.func();
//^^^^^^^^ type: ()
}
"#,
);
}

View file

@ -1080,3 +1080,15 @@ fn my_fn(#[cfg(feature = "feature")] u8: u8, u32: u32) {}
"#, "#,
); );
} }
#[test]
fn var_args() {
check_types(
r#"
#[lang = "va_list"]
pub struct VaListImpl<'f>;
fn my_fn(foo: ...) {}
//^^^ VaListImpl
"#,
);
}

View file

@ -1723,3 +1723,24 @@ fn bar() -> ControlFlow<(), ()> {
"#, "#,
); );
} }
#[test]
fn assoc_type_shorthand_with_gats_in_binders() {
// c.f. test `issue_4885()`
check_no_mismatches(
r#"
trait Gats {
type Assoc<T>;
}
trait Foo<T> {}
struct Bar<'a, B: Gats, A> {
field: &'a dyn Foo<B::Assoc<A>>,
}
fn foo(b: Bar) {
let _ = b.field;
}
"#,
);
}

View file

@ -2064,17 +2064,17 @@ fn fn_pointer_return() {
fn block_modifiers_smoke_test() { fn block_modifiers_smoke_test() {
check_infer( check_infer(
r#" r#"
//- minicore: future //- minicore: future, try
async fn main() { async fn main() {
let x = unsafe { 92 }; let x = unsafe { 92 };
let y = async { async { () }.await }; let y = async { async { () }.await };
let z = try { () }; let z: core::ops::ControlFlow<(), _> = try { () };
let w = const { 92 }; let w = const { 92 };
let t = 'a: { 92 }; let t = 'a: { 92 };
} }
"#, "#,
expect![[r#" expect![[r#"
16..162 '{ ...2 }; }': () 16..193 '{ ...2 }; }': ()
26..27 'x': i32 26..27 'x': i32
30..43 'unsafe { 92 }': i32 30..43 'unsafe { 92 }': i32
30..43 'unsafe { 92 }': i32 30..43 'unsafe { 92 }': i32
@ -2086,17 +2086,17 @@ async fn main() {
65..77 'async { () }': impl Future<Output = ()> 65..77 'async { () }': impl Future<Output = ()>
65..83 'async ....await': () 65..83 'async ....await': ()
73..75 '()': () 73..75 '()': ()
95..96 'z': {unknown} 95..96 'z': ControlFlow<(), ()>
99..109 'try { () }': () 130..140 'try { () }': ()
99..109 'try { () }': {unknown} 130..140 'try { () }': ControlFlow<(), ()>
105..107 '()': () 136..138 '()': ()
119..120 'w': i32 150..151 'w': i32
123..135 'const { 92 }': i32 154..166 'const { 92 }': i32
123..135 'const { 92 }': i32 154..166 'const { 92 }': i32
131..133 '92': i32 162..164 '92': i32
145..146 't': i32 176..177 't': i32
149..159 ''a: { 92 }': i32 180..190 ''a: { 92 }': i32
155..157 '92': i32 186..188 '92': i32
"#]], "#]],
) )
} }

View file

@ -1388,6 +1388,22 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
); );
} }
#[test]
fn return_pos_impl_trait_in_projection() {
// Note that the unused type param `X` is significant; see #13307.
check_no_mismatches(
r#"
//- minicore: sized
trait Future { type Output; }
impl Future for () { type Output = i32; }
type Foo<F> = (<F as Future>::Output, F);
fn foo<X>() -> Foo<impl Future<Output = ()>> {
(0, ())
}
"#,
)
}
#[test] #[test]
fn dyn_trait() { fn dyn_trait() {
check_infer( check_infer(
@ -4084,3 +4100,68 @@ where
"#, "#,
); );
} }
#[test]
fn bin_op_with_scalar_fallback() {
// Extra impls are significant so that chalk doesn't give us definite guidances.
check_types(
r#"
//- minicore: add
use core::ops::Add;
struct Vec2<T>(T, T);
impl Add for Vec2<i32> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output { loop {} }
}
impl Add for Vec2<u32> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output { loop {} }
}
impl Add for Vec2<f32> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output { loop {} }
}
impl Add for Vec2<f64> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output { loop {} }
}
fn test() {
let a = Vec2(1, 2);
let b = Vec2(3, 4);
let c = a + b;
//^ Vec2<i32>
let a = Vec2(1., 2.);
let b = Vec2(3., 4.);
let c = a + b;
//^ Vec2<f64>
}
"#,
);
}
#[test]
fn trait_method_with_scalar_fallback() {
check_types(
r#"
trait Trait {
type Output;
fn foo(&self) -> Self::Output;
}
impl<T> Trait for T {
type Output = T;
fn foo(&self) -> Self::Output { loop {} }
}
fn test() {
let a = 42;
let b = a.foo();
//^ i32
let a = 3.14;
let b = a.foo();
//^ f64
}
"#,
);
}

View file

@ -67,12 +67,12 @@ impl DebugContext<'_> {
let trait_ref = projection_ty.trait_ref(self.0); let trait_ref = projection_ty.trait_ref(self.0);
let trait_params = trait_ref.substitution.as_slice(Interner); let trait_params = trait_ref.substitution.as_slice(Interner);
let self_ty = trait_ref.self_type_parameter(Interner); let self_ty = trait_ref.self_type_parameter(Interner);
write!(fmt, "<{:?} as {}", self_ty, trait_name)?; write!(fmt, "<{self_ty:?} as {trait_name}")?;
if trait_params.len() > 1 { if trait_params.len() > 1 {
write!( write!(
fmt, fmt,
"<{}>", "<{}>",
trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))), trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))),
)?; )?;
} }
write!(fmt, ">::{}", type_alias_data.name)?; write!(fmt, ">::{}", type_alias_data.name)?;
@ -83,7 +83,7 @@ impl DebugContext<'_> {
write!( write!(
fmt, fmt,
"<{}>", "<{}>",
proj_params.iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))), proj_params.iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))),
)?; )?;
} }
@ -105,9 +105,9 @@ impl DebugContext<'_> {
} }
}; };
match def { match def {
CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name), CallableDefId::FunctionId(_) => write!(fmt, "{{fn {name}}}"),
CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => { CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {
write!(fmt, "{{ctor {}}}", name) write!(fmt, "{{ctor {name}}}")
} }
} }
} }

View file

@ -18,7 +18,7 @@ use crate::{
}; };
/// This controls how much 'time' we give the Chalk solver before giving up. /// This controls how much 'time' we give the Chalk solver before giving up.
const CHALK_SOLVER_FUEL: i32 = 100; const CHALK_SOLVER_FUEL: i32 = 1000;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub(crate) struct ChalkContext<'a> { pub(crate) struct ChalkContext<'a> {
@ -55,13 +55,10 @@ impl TraitEnvironment {
} }
} }
pub fn traits_in_scope_from_clauses<'a>( pub fn traits_in_scope_from_clauses(&self, ty: Ty) -> impl Iterator<Item = TraitId> + '_ {
&'a self,
ty: Ty,
) -> impl Iterator<Item = TraitId> + 'a {
self.traits_from_clauses self.traits_from_clauses
.iter() .iter()
.filter_map(move |(self_ty, trait_id)| (*self_ty == ty).then(|| *trait_id)) .filter_map(move |(self_ty, trait_id)| (*self_ty == ty).then_some(*trait_id))
} }
} }
@ -130,7 +127,7 @@ fn solve(
let mut solve = || { let mut solve = || {
let _ctx = if is_chalk_debug() || is_chalk_print() { let _ctx = if is_chalk_debug() || is_chalk_print() {
Some(panic_context::enter(format!("solving {:?}", goal))) Some(panic_context::enter(format!("solving {goal:?}")))
} else { } else {
None None
}; };

View file

@ -17,7 +17,7 @@ use hir_def::{
ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId, ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId,
TypeOrConstParamId, TypeParamId, TypeOrConstParamId, TypeParamId,
}; };
use hir_expand::name::{known, Name}; use hir_expand::name::Name;
use itertools::Either; use itertools::Either;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
@ -184,9 +184,7 @@ pub(crate) struct Generics {
} }
impl Generics { impl Generics {
pub(crate) fn iter_id<'a>( pub(crate) fn iter_id(&self) -> impl Iterator<Item = Either<TypeParamId, ConstParamId>> + '_ {
&'a self,
) -> impl Iterator<Item = Either<TypeParamId, ConstParamId>> + 'a {
self.iter().map(|(id, data)| match data { self.iter().map(|(id, data)| match data {
TypeOrConstParamData::TypeParamData(_) => Either::Left(TypeParamId::from_unchecked(id)), TypeOrConstParamData::TypeParamData(_) => Either::Left(TypeParamId::from_unchecked(id)),
TypeOrConstParamData::ConstParamData(_) => { TypeOrConstParamData::ConstParamData(_) => {
@ -216,9 +214,9 @@ impl Generics {
} }
/// Iterator over types and const params of parent. /// Iterator over types and const params of parent.
pub(crate) fn iter_parent<'a>( pub(crate) fn iter_parent(
&'a self, &self,
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a { ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &TypeOrConstParamData)> {
self.parent_generics().into_iter().flat_map(|it| { self.parent_generics().into_iter().flat_map(|it| {
let to_toc_id = let to_toc_id =
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p); move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p);
@ -335,54 +333,18 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
// Function in an `extern` block are always unsafe to call, except when it has // Function in an `extern` block are always unsafe to call, except when it has
// `"rust-intrinsic"` ABI there are a few exceptions. // `"rust-intrinsic"` ABI there are a few exceptions.
let id = block.lookup(db.upcast()).id; let id = block.lookup(db.upcast()).id;
!matches!(
id.item_tree(db.upcast())[id.value].abi.as_deref(), let is_intrinsic =
Some("rust-intrinsic") if !is_intrinsic_fn_unsafe(&data.name) id.item_tree(db.upcast())[id.value].abi.as_deref() == Some("rust-intrinsic");
)
if is_intrinsic {
// Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
!data.attrs.by_key("rustc_safe_intrinsic").exists()
} else {
// Extern items are always unsafe
true
}
} }
_ => false, _ => false,
} }
} }
/// Returns `true` if the given intrinsic is unsafe to call, or false otherwise.
fn is_intrinsic_fn_unsafe(name: &Name) -> bool {
// Should be kept in sync with https://github.com/rust-lang/rust/blob/532d2b14c05f9bc20b2d27cbb5f4550d28343a36/compiler/rustc_typeck/src/check/intrinsic.rs#L72-L106
![
known::abort,
known::add_with_overflow,
known::bitreverse,
known::black_box,
known::bswap,
known::caller_location,
known::ctlz,
known::ctpop,
known::cttz,
known::discriminant_value,
known::forget,
known::likely,
known::maxnumf32,
known::maxnumf64,
known::min_align_of,
known::minnumf32,
known::minnumf64,
known::mul_with_overflow,
known::needs_drop,
known::ptr_guaranteed_eq,
known::ptr_guaranteed_ne,
known::rotate_left,
known::rotate_right,
known::rustc_peek,
known::saturating_add,
known::saturating_sub,
known::size_of,
known::sub_with_overflow,
known::type_id,
known::type_name,
known::unlikely,
known::variant_count,
known::wrapping_add,
known::wrapping_mul,
known::wrapping_sub,
]
.contains(name)
}

View file

@ -148,7 +148,7 @@ fn resolve_doc_path(
let modpath = { let modpath = {
// FIXME: this is not how we should get a mod path here // FIXME: this is not how we should get a mod path here
let ast_path = ast::SourceFile::parse(&format!("type T = {};", link)) let ast_path = ast::SourceFile::parse(&format!("type T = {link};"))
.syntax_node() .syntax_node()
.descendants() .descendants()
.find_map(ast::Path::cast)?; .find_map(ast::Path::cast)?;

View file

@ -10,7 +10,7 @@ use hir_def::path::ModPath;
use hir_expand::{name::Name, HirFileId, InFile}; use hir_expand::{name::Name, HirFileId, InFile};
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
use crate::{MacroKind, Type}; use crate::{AssocItem, Field, MacroKind, Type};
macro_rules! diagnostics { macro_rules! diagnostics {
($($diag:ident,)*) => { ($($diag:ident,)*) => {
@ -41,6 +41,8 @@ diagnostics![
MissingMatchArms, MissingMatchArms,
MissingUnsafe, MissingUnsafe,
NoSuchField, NoSuchField,
PrivateAssocItem,
PrivateField,
ReplaceFilterMapNextWithFindMap, ReplaceFilterMapNextWithFindMap,
TypeMismatch, TypeMismatch,
UnimplementedBuiltinMacro, UnimplementedBuiltinMacro,
@ -121,6 +123,19 @@ pub struct NoSuchField {
pub field: InFile<AstPtr<ast::RecordExprField>>, pub field: InFile<AstPtr<ast::RecordExprField>>,
} }
#[derive(Debug)]
pub struct PrivateAssocItem {
pub expr_or_pat:
InFile<Either<AstPtr<ast::Expr>, Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>>>,
pub item: AssocItem,
}
#[derive(Debug)]
pub struct PrivateField {
pub expr: InFile<AstPtr<ast::Expr>>,
pub field: Field,
}
#[derive(Debug)] #[derive(Debug)]
pub struct BreakOutsideOfLoop { pub struct BreakOutsideOfLoop {
pub expr: InFile<AstPtr<ast::Expr>>, pub expr: InFile<AstPtr<ast::Expr>>,

View file

@ -79,7 +79,7 @@ impl HirDisplay for Function {
} }
} }
match name { match name {
Some(name) => write!(f, "{}: ", name)?, Some(name) => write!(f, "{name}: ")?,
None => f.write_str("_: ")?, None => f.write_str("_: ")?,
} }
// FIXME: Use resolved `param.ty` or raw `type_ref`? // FIXME: Use resolved `param.ty` or raw `type_ref`?
@ -327,7 +327,7 @@ fn write_generic_params(
continue; continue;
} }
delim(f)?; delim(f)?;
write!(f, "{}", name)?; write!(f, "{name}")?;
if let Some(default) = &ty.default { if let Some(default) = &ty.default {
f.write_str(" = ")?; f.write_str(" = ")?;
default.hir_fmt(f)?; default.hir_fmt(f)?;
@ -335,7 +335,7 @@ fn write_generic_params(
} }
TypeOrConstParamData::ConstParamData(c) => { TypeOrConstParamData::ConstParamData(c) => {
delim(f)?; delim(f)?;
write!(f, "const {}: ", name)?; write!(f, "const {name}: ")?;
c.ty.hir_fmt(f)?; c.ty.hir_fmt(f)?;
} }
} }
@ -372,7 +372,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f), WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
WherePredicateTypeTarget::TypeOrConstParam(id) => { WherePredicateTypeTarget::TypeOrConstParam(id) => {
match &params.type_or_consts[*id].name() { match &params.type_or_consts[*id].name() {
Some(name) => write!(f, "{}", name), Some(name) => write!(f, "{name}"),
None => f.write_str("{unnamed}"), None => f.write_str("{unnamed}"),
} }
} }
@ -424,7 +424,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
if idx != 0 { if idx != 0 {
f.write_str(", ")?; f.write_str(", ")?;
} }
write!(f, "{}", lifetime)?; write!(f, "{lifetime}")?;
} }
f.write_str("> ")?; f.write_str("> ")?;
write_target(target, f)?; write_target(target, f)?;
@ -447,7 +447,7 @@ impl HirDisplay for Const {
let data = f.db.const_data(self.id); let data = f.db.const_data(self.id);
f.write_str("const ")?; f.write_str("const ")?;
match &data.name { match &data.name {
Some(name) => write!(f, "{}: ", name)?, Some(name) => write!(f, "{name}: ")?,
None => f.write_str("_: ")?, None => f.write_str("_: ")?,
} }
data.type_ref.hir_fmt(f)?; data.type_ref.hir_fmt(f)?;
@ -511,9 +511,9 @@ impl HirDisplay for Module {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
// FIXME: Module doesn't have visibility saved in data. // FIXME: Module doesn't have visibility saved in data.
match self.name(f.db) { match self.name(f.db) {
Some(name) => write!(f, "mod {}", name), Some(name) => write!(f, "mod {name}"),
None if self.is_crate_root(f.db) => match self.krate(f.db).display_name(f.db) { None if self.is_crate_root(f.db) => match self.krate(f.db).display_name(f.db) {
Some(name) => write!(f, "extern crate {}", name), Some(name) => write!(f, "extern crate {name}"),
None => f.write_str("extern crate {unknown}"), None => f.write_str("extern crate {unknown}"),
}, },
None => f.write_str("mod {unnamed}"), None => f.write_str("mod {unnamed}"),

View file

@ -39,12 +39,13 @@ use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind}; use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
use either::Either; use either::Either;
use hir_def::{ use hir_def::{
adt::{ReprData, VariantData}, adt::VariantData,
body::{BodyDiagnostic, SyntheticSyntax}, body::{BodyDiagnostic, SyntheticSyntax},
expr::{BindingAnnotation, LabelId, Pat, PatId}, expr::{BindingAnnotation, ExprOrPatId, LabelId, Pat, PatId},
generics::{TypeOrConstParamData, TypeParamProvenance}, generics::{TypeOrConstParamData, TypeParamProvenance},
item_tree::ItemTreeNode, item_tree::ItemTreeNode,
lang_item::LangItemTarget, lang_item::LangItemTarget,
layout::{Layout, LayoutError, ReprOptions},
nameres::{self, diagnostics::DefDiagnostic}, nameres::{self, diagnostics::DefDiagnostic},
per_ns::PerNs, per_ns::PerNs,
resolver::{HasResolver, Resolver}, resolver::{HasResolver, Resolver},
@ -59,6 +60,7 @@ use hir_ty::{
all_super_traits, autoderef, all_super_traits, autoderef,
consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt}, consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt},
diagnostics::BodyValidationDiagnostic, diagnostics::BodyValidationDiagnostic,
layout::layout_of_ty,
method_resolution::{self, TyFingerprint}, method_resolution::{self, TyFingerprint},
primitive::UintTy, primitive::UintTy,
traits::FnTrait, traits::FnTrait,
@ -72,7 +74,7 @@ use once_cell::unsync::Lazy;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use stdx::{impl_from, never}; use stdx::{impl_from, never};
use syntax::{ use syntax::{
ast::{self, Expr, HasAttrs as _, HasDocComments, HasName}, ast::{self, HasAttrs as _, HasDocComments, HasName},
AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T, AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
}; };
@ -83,9 +85,10 @@ pub use crate::{
diagnostics::{ diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
MissingUnsafe, NoSuchField, ReplaceFilterMapNextWithFindMap, TypeMismatch, MissingUnsafe, NoSuchField, PrivateAssocItem, PrivateField,
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
UnresolvedModule, UnresolvedProcMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
UnresolvedProcMacro,
}, },
has_source::HasSource, has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@ -112,12 +115,20 @@ pub use {
path::{ModPath, PathKind}, path::{ModPath, PathKind},
type_ref::{Mutability, TypeRef}, type_ref::{Mutability, TypeRef},
visibility::Visibility, visibility::Visibility,
// FIXME: This is here since it is input of a method in `HirWrite`
// and things outside of hir need to implement that trait. We probably
// should move whole `hir_ty::display` to this crate so we will become
// able to use `ModuleDef` or `Definition` instead of `ModuleDefId`.
ModuleDefId,
}, },
hir_expand::{ hir_expand::{
name::{known, Name}, name::{known, Name},
ExpandResult, HirFileId, InFile, MacroFile, Origin, ExpandResult, HirFileId, InFile, MacroFile, Origin,
}, },
hir_ty::{display::HirDisplay, PointerCast, Safety}, hir_ty::{
display::{HirDisplay, HirWrite},
PointerCast, Safety,
},
}; };
// These are negative re-exports: pub using these names is forbidden, they // These are negative re-exports: pub using these names is forbidden, they
@ -597,7 +608,7 @@ impl Module {
pub fn legacy_macros(self, db: &dyn HirDatabase) -> Vec<Macro> { pub fn legacy_macros(self, db: &dyn HirDatabase) -> Vec<Macro> {
let def_map = self.id.def_map(db.upcast()); let def_map = self.id.def_map(db.upcast());
let scope = &def_map[self.id.local_id].scope; let scope = &def_map[self.id.local_id].scope;
scope.legacy_macros().flat_map(|(_, it)| it).map(|&it| MacroId::from(it).into()).collect() scope.legacy_macros().flat_map(|(_, it)| it).map(|&it| it.into()).collect()
} }
pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> { pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> {
@ -803,7 +814,7 @@ fn precise_macro_call_location(
.doc_comments_and_attrs() .doc_comments_and_attrs()
.nth((*invoc_attr_index) as usize) .nth((*invoc_attr_index) as usize)
.and_then(Either::left) .and_then(Either::left)
.unwrap_or_else(|| panic!("cannot find attribute #{}", invoc_attr_index)); .unwrap_or_else(|| panic!("cannot find attribute #{invoc_attr_index}"));
( (
ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))), ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))),
@ -844,6 +855,10 @@ impl Field {
self.parent.variant_data(db).fields()[self.id].name.clone() self.parent.variant_data(db).fields()[self.id].name.clone()
} }
pub fn index(&self) -> usize {
u32::from(self.id.into_raw()) as usize
}
/// Returns the type as in the signature of the struct (i.e., with /// Returns the type as in the signature of the struct (i.e., with
/// placeholder types for type parameters). Only use this in the context of /// placeholder types for type parameters). Only use this in the context of
/// the field definition. /// the field definition.
@ -859,6 +874,10 @@ impl Field {
Type::new(db, var_id, ty) Type::new(db, var_id, ty)
} }
pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
layout_of_ty(db, &self.ty(db).ty, self.parent.module(db).krate().into())
}
pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
self.parent self.parent
} }
@ -900,7 +919,7 @@ impl Struct {
Type::from_def(db, self.id) Type::from_def(db, self.id)
} }
pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprData> { pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
db.struct_data(self.id).repr.clone() db.struct_data(self.id).repr.clone()
} }
@ -984,8 +1003,30 @@ impl Enum {
Type::new_for_crate( Type::new_for_crate(
self.id.lookup(db.upcast()).container.krate(), self.id.lookup(db.upcast()).container.krate(),
TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() { TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() {
Either::Left(builtin) => hir_def::builtin_type::BuiltinType::Int(builtin), hir_def::layout::IntegerType::Pointer(sign) => match sign {
Either::Right(builtin) => hir_def::builtin_type::BuiltinType::Uint(builtin), true => hir_def::builtin_type::BuiltinType::Int(
hir_def::builtin_type::BuiltinInt::Isize,
),
false => hir_def::builtin_type::BuiltinType::Uint(
hir_def::builtin_type::BuiltinUint::Usize,
),
},
hir_def::layout::IntegerType::Fixed(i, sign) => match sign {
true => hir_def::builtin_type::BuiltinType::Int(match i {
hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8,
hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16,
hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32,
hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64,
hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128,
}),
false => hir_def::builtin_type::BuiltinType::Uint(match i {
hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8,
hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16,
hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32,
hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64,
hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128,
}),
},
}), }),
) )
} }
@ -1042,7 +1083,7 @@ impl Variant {
db.enum_data(self.parent.id).variants[self.id].variant_data.clone() db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
} }
pub fn value(self, db: &dyn HirDatabase) -> Option<Expr> { pub fn value(self, db: &dyn HirDatabase) -> Option<ast::Expr> {
self.source(db)?.value.expr() self.source(db)?.value.expr()
} }
@ -1076,6 +1117,13 @@ impl Adt {
}) })
} }
pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
if db.generic_params(self.into()).iter().count() != 0 {
return Err(LayoutError::HasPlaceholder);
}
db.layout_of_adt(self.into(), Substitution::empty(Interner))
}
/// Turns this ADT into a type. Any type parameters of the ADT will be /// Turns this ADT into a type. Any type parameters of the ADT will be
/// turned into unknown types, which is good for e.g. finding the most /// turned into unknown types, which is good for e.g. finding the most
/// general set of completions, but will not look very nice when printed. /// general set of completions, but will not look very nice when printed.
@ -1306,6 +1354,25 @@ impl DefWithBody {
Err(SyntheticSyntax) => (), Err(SyntheticSyntax) => (),
} }
} }
&hir_ty::InferenceDiagnostic::PrivateField { expr, field } => {
let expr = source_map.expr_syntax(expr).expect("unexpected synthetic");
let field = field.into();
acc.push(PrivateField { expr, field }.into())
}
&hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => {
let expr_or_pat = match id {
ExprOrPatId::ExprId(expr) => source_map
.expr_syntax(expr)
.expect("unexpected synthetic")
.map(Either::Left),
ExprOrPatId::PatId(pat) => source_map
.pat_syntax(pat)
.expect("unexpected synthetic")
.map(Either::Right),
};
let item = item.into();
acc.push(PrivateAssocItem { expr_or_pat, item }.into())
}
} }
} }
for (expr, mismatch) in infer.expr_type_mismatches() { for (expr, mismatch) in infer.expr_type_mismatches() {
@ -1492,7 +1559,7 @@ impl Function {
} }
pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
self.has_self_param(db).then(|| SelfParam { func: self.id }) self.has_self_param(db).then_some(SelfParam { func: self.id })
} }
pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> { pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
@ -2344,17 +2411,19 @@ pub struct DeriveHelper {
impl DeriveHelper { impl DeriveHelper {
pub fn derive(&self) -> Macro { pub fn derive(&self) -> Macro {
Macro { id: self.derive.into() } Macro { id: self.derive }
} }
pub fn name(&self, db: &dyn HirDatabase) -> Name { pub fn name(&self, db: &dyn HirDatabase) -> Name {
match self.derive { match self.derive {
MacroId::Macro2Id(_) => None, MacroId::Macro2Id(it) => {
db.macro2_data(it).helpers.as_deref().and_then(|it| it.get(self.idx)).cloned()
}
MacroId::MacroRulesId(_) => None, MacroId::MacroRulesId(_) => None,
MacroId::ProcMacroId(proc_macro) => db MacroId::ProcMacroId(proc_macro) => db
.proc_macro_data(proc_macro) .proc_macro_data(proc_macro)
.helpers .helpers
.as_ref() .as_deref()
.and_then(|it| it.get(self.idx)) .and_then(|it| it.get(self.idx))
.cloned(), .cloned(),
} }
@ -2712,7 +2781,7 @@ impl Impl {
pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec<Impl> { pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec<Impl> {
let krate = trait_.module(db).krate(); let krate = trait_.module(db).krate();
let mut all = Vec::new(); let mut all = Vec::new();
for Crate { id } in krate.transitive_reverse_dependencies(db).into_iter() { for Crate { id } in krate.transitive_reverse_dependencies(db) {
let impls = db.trait_impls_in_crate(id); let impls = db.trait_impls_in_crate(id);
all.extend(impls.for_trait(trait_.id).map(Self::from)) all.extend(impls.for_trait(trait_.id).map(Self::from))
} }
@ -2857,6 +2926,13 @@ impl Type {
matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Uint(UintTy::Usize))) matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Uint(UintTy::Usize)))
} }
pub fn is_int_or_uint(&self) -> bool {
match self.ty.kind(Interner) {
TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_)) => true,
_ => false,
}
}
pub fn remove_ref(&self) -> Option<Type> { pub fn remove_ref(&self) -> Option<Type> {
match &self.ty.kind(Interner) { match &self.ty.kind(Interner) {
TyKind::Ref(.., ty) => Some(self.derived(ty.clone())), TyKind::Ref(.., ty) => Some(self.derived(ty.clone())),
@ -3031,7 +3107,7 @@ impl Type {
let adt = adt_id.into(); let adt = adt_id.into();
match adt { match adt {
Adt::Struct(s) => matches!(s.repr(db), Some(ReprData { packed: true, .. })), Adt::Struct(s) => s.repr(db).unwrap_or_default().pack.is_some(),
_ => false, _ => false,
} }
} }
@ -3225,7 +3301,7 @@ impl Type {
with_local_impls.and_then(|b| b.id.containing_block()).into(), with_local_impls.and_then(|b| b.id.containing_block()).into(),
name, name,
method_resolution::LookupMode::MethodCall, method_resolution::LookupMode::MethodCall,
&mut |_adj, id| callback(id), &mut |_adj, id, _| callback(id),
); );
} }
@ -3650,6 +3726,13 @@ impl From<ItemInNs> for ScopeDef {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Adjustment {
pub source: Type,
pub target: Type,
pub kind: Adjust,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Adjust { pub enum Adjust {
/// Go from ! to any type. /// Go from ! to any type.

View file

@ -2,14 +2,17 @@
mod source_to_def; mod source_to_def;
use std::{cell::RefCell, fmt, iter, ops}; use std::{cell::RefCell, fmt, iter, mem, ops};
use base_db::{FileId, FileRange}; use base_db::{FileId, FileRange};
use either::Either;
use hir_def::{ use hir_def::{
body, macro_id_to_def_id, body,
expr::Expr,
macro_id_to_def_id,
resolver::{self, HasResolver, Resolver, TypeNs}, resolver::{self, HasResolver, Resolver, TypeNs},
type_ref::Mutability, type_ref::Mutability,
AsMacroCall, FunctionId, MacroId, TraitId, VariantId, AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
}; };
use hir_expand::{ use hir_expand::{
db::AstDatabase, db::AstDatabase,
@ -29,7 +32,7 @@ use crate::{
db::HirDatabase, db::HirDatabase,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, SourceAnalyzer}, source_analyzer::{resolve_hir_path, SourceAnalyzer},
Access, Adjust, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, Access, Adjust, Adjustment, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate,
DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local,
Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, ToolModule, Trait, Type, Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, ToolModule, Trait, Type,
TypeAlias, TypeParam, VariantDef, TypeAlias, TypeParam, VariantDef,
@ -334,7 +337,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.resolve_trait(trait_) self.imp.resolve_trait(trait_)
} }
pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjust>> { pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
self.imp.expr_adjustments(expr) self.imp.expr_adjustments(expr)
} }
@ -438,8 +441,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
} }
pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> { pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
let src = self.imp.find_file(src.syntax()).with_value(src).cloned(); self.imp.to_def(src)
T::to_def(&self.imp, src)
} }
pub fn to_module_def(&self, file: FileId) -> Option<Module> { pub fn to_module_def(&self, file: FileId) -> Option<Module> {
@ -481,6 +483,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
self.imp.is_unsafe_ident_pat(ident_pat) self.imp.is_unsafe_ident_pat(ident_pat)
} }
/// Returns `true` if the `node` is inside an `unsafe` context.
pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
self.imp.is_inside_unsafe(expr)
}
} }
impl<'db> SemanticsImpl<'db> { impl<'db> SemanticsImpl<'db> {
@ -788,7 +795,7 @@ impl<'db> SemanticsImpl<'db> {
// requeue the tokens we got from mapping our current token down // requeue the tokens we got from mapping our current token down
stack.extend(mapped_tokens); stack.extend(mapped_tokens);
// if the length changed we have found a mapping for the token // if the length changed we have found a mapping for the token
(stack.len() != len).then(|| ()) (stack.len() != len).then_some(())
}; };
// Remap the next token in the queue into a macro call its in, if it is not being remapped // Remap the next token in the queue into a macro call its in, if it is not being remapped
@ -840,7 +847,7 @@ impl<'db> SemanticsImpl<'db> {
} }
}; };
process_expansion_for_token(&mut stack, file_id, None, token.as_ref()) process_expansion_for_token(&mut stack, file_id, None, token.as_ref())
} else if let Some(meta) = ast::Meta::cast(parent.clone()) { } else if let Some(meta) = ast::Meta::cast(parent) {
// attribute we failed expansion for earlier, this might be a derive invocation // attribute we failed expansion for earlier, this might be a derive invocation
// or derive helper attribute // or derive helper attribute
let attr = meta.parent_attr()?; let attr = meta.parent_attr()?;
@ -1067,26 +1074,42 @@ impl<'db> SemanticsImpl<'db> {
} }
} }
fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjust>> { fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
let mutability = |m| match m { let mutability = |m| match m {
hir_ty::Mutability::Not => Mutability::Shared, hir_ty::Mutability::Not => Mutability::Shared,
hir_ty::Mutability::Mut => Mutability::Mut, hir_ty::Mutability::Mut => Mutability::Mut,
}; };
self.analyze(expr.syntax())?.expr_adjustments(self.db, expr).map(|it| {
let analyzer = self.analyze(expr.syntax())?;
let (mut source_ty, _) = analyzer.type_of_expr(self.db, expr)?;
analyzer.expr_adjustments(self.db, expr).map(|it| {
it.iter() it.iter()
.map(|adjust| match adjust.kind { .map(|adjust| {
hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, let target =
hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { Type::new_with_resolver(self.db, &analyzer.resolver, adjust.target.clone());
Adjust::Deref(Some(OverloadedDeref(mutability(m)))) let kind = match adjust.kind {
} hir_ty::Adjust::NeverToAny => Adjust::NeverToAny,
hir_ty::Adjust::Deref(None) => Adjust::Deref(None), hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => {
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => { Adjust::Deref(Some(OverloadedDeref(mutability(m))))
Adjust::Borrow(AutoBorrow::RawPtr(mutability(m))) }
} hir_ty::Adjust::Deref(None) => Adjust::Deref(None),
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(m)) => { hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
Adjust::Borrow(AutoBorrow::Ref(mutability(m))) Adjust::Borrow(AutoBorrow::RawPtr(mutability(m)))
} }
hir_ty::Adjust::Pointer(pc) => Adjust::Pointer(pc), hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(m)) => {
Adjust::Borrow(AutoBorrow::Ref(mutability(m)))
}
hir_ty::Adjust::Pointer(pc) => Adjust::Pointer(pc),
};
// Update `source_ty` for the next adjustment
let source = mem::replace(&mut source_ty, target.clone());
let adjustment = Adjustment { source, target, kind };
adjustment
}) })
.collect() .collect()
}) })
@ -1198,7 +1221,7 @@ impl<'db> SemanticsImpl<'db> {
krate krate
.dependencies(self.db) .dependencies(self.db)
.into_iter() .into_iter()
.find_map(|dep| (dep.name == name).then(|| dep.krate)) .find_map(|dep| (dep.name == name).then_some(dep.krate))
} }
fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> { fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> {
@ -1223,10 +1246,15 @@ impl<'db> SemanticsImpl<'db> {
fn with_ctx<F: FnOnce(&mut SourceToDefCtx<'_, '_>) -> T, T>(&self, f: F) -> T { fn with_ctx<F: FnOnce(&mut SourceToDefCtx<'_, '_>) -> T, T>(&self, f: F) -> T {
let mut cache = self.s2d_cache.borrow_mut(); let mut cache = self.s2d_cache.borrow_mut();
let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache }; let mut ctx = SourceToDefCtx { db: self.db, cache: &mut cache };
f(&mut ctx) f(&mut ctx)
} }
fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
let src = self.find_file(src.syntax()).with_value(src).cloned();
T::to_def(self, src)
}
fn to_module_def(&self, file: FileId) -> impl Iterator<Item = Module> { fn to_module_def(&self, file: FileId) -> impl Iterator<Item = Module> {
self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from) self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from)
} }
@ -1350,7 +1378,7 @@ impl<'db> SemanticsImpl<'db> {
self.cache self.cache
.borrow() .borrow()
.keys() .keys()
.map(|it| format!("{:?}", it)) .map(|it| format!("{it:?}"))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
) )
@ -1442,6 +1470,56 @@ impl<'db> SemanticsImpl<'db> {
.map(|ty| ty.original.is_packed(self.db)) .map(|ty| ty.original.is_packed(self.db))
.unwrap_or(false) .unwrap_or(false)
} }
fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
let item_or_variant = |ancestor: SyntaxNode| {
if ast::Item::can_cast(ancestor.kind()) {
ast::Item::cast(ancestor).map(Either::Left)
} else {
ast::Variant::cast(ancestor).map(Either::Right)
}
};
let Some(enclosing_item) = expr.syntax().ancestors().find_map(item_or_variant) else { return false };
let def = match &enclosing_item {
Either::Left(ast::Item::Fn(it)) if it.unsafe_token().is_some() => return true,
Either::Left(ast::Item::Fn(it)) => {
self.to_def(it).map(<_>::into).map(DefWithBodyId::FunctionId)
}
Either::Left(ast::Item::Const(it)) => {
self.to_def(it).map(<_>::into).map(DefWithBodyId::ConstId)
}
Either::Left(ast::Item::Static(it)) => {
self.to_def(it).map(<_>::into).map(DefWithBodyId::StaticId)
}
Either::Left(_) => None,
Either::Right(it) => self.to_def(it).map(<_>::into).map(DefWithBodyId::VariantId),
};
let Some(def) = def else { return false };
let enclosing_node = enclosing_item.as_ref().either(|i| i.syntax(), |v| v.syntax());
let (body, source_map) = self.db.body_with_source_map(def);
let file_id = self.find_file(expr.syntax()).file_id;
let Some(mut parent) = expr.syntax().parent() else { return false };
loop {
if &parent == enclosing_node {
break false;
}
if let Some(parent) = ast::Expr::cast(parent.clone()) {
if let Some(expr_id) = source_map.node_expr(InFile { file_id, value: &parent }) {
if let Expr::Unsafe { .. } = body[expr_id] {
break true;
}
}
}
let Some(parent_) = parent.parent() else { break false };
parent = parent_;
}
}
} }
fn macro_call_to_macro_id( fn macro_call_to_macro_id(
@ -1600,7 +1678,7 @@ impl<'a> SemanticsScope<'a> {
self.db, self.db,
def, def,
resolution.in_type_ns()?, resolution.in_type_ns()?,
|name, _, id| cb(name, id.into()), |name, id| cb(name, id.into()),
) )
} }
} }

View file

@ -21,8 +21,8 @@ use hir_def::{
path::{ModPath, Path, PathKind}, path::{ModPath, Path, PathKind},
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
type_ref::Mutability, type_ref::Mutability,
AsMacroCall, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId, AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId,
Lookup, ModuleDefId, TraitId, VariantId, LocalFieldId, Lookup, ModuleDefId, TraitId, VariantId,
}; };
use hir_expand::{ use hir_expand::{
builtin_fn_macro::BuiltinFnLikeExpander, builtin_fn_macro::BuiltinFnLikeExpander,
@ -118,7 +118,7 @@ impl SourceAnalyzer {
fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprId> { fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprId> {
let src = match expr { let src = match expr {
ast::Expr::MacroExpr(expr) => { ast::Expr::MacroExpr(expr) => {
self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?.clone()))? self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))?
} }
_ => InFile::new(self.file_id, expr.clone()), _ => InFile::new(self.file_id, expr.clone()),
}; };
@ -228,7 +228,7 @@ impl SourceAnalyzer {
db: &dyn HirDatabase, db: &dyn HirDatabase,
pat: &ast::Pat, pat: &ast::Pat,
) -> Option<SmallVec<[Type; 1]>> { ) -> Option<SmallVec<[Type; 1]>> {
let pat_id = self.pat_id(&pat)?; let pat_id = self.pat_id(pat)?;
let infer = self.infer.as_ref()?; let infer = self.infer.as_ref()?;
Some( Some(
infer infer
@ -270,7 +270,7 @@ impl SourceAnalyzer {
db: &dyn HirDatabase, db: &dyn HirDatabase,
await_expr: &ast::AwaitExpr, await_expr: &ast::AwaitExpr,
) -> Option<FunctionId> { ) -> Option<FunctionId> {
let mut ty = self.ty_of_expr(db, &await_expr.expr()?.into())?.clone(); let mut ty = self.ty_of_expr(db, &await_expr.expr()?)?.clone();
let into_future_trait = self let into_future_trait = self
.resolver .resolver
@ -316,7 +316,7 @@ impl SourceAnalyzer {
ast::UnaryOp::Not => name![not], ast::UnaryOp::Not => name![not],
ast::UnaryOp::Neg => name![neg], ast::UnaryOp::Neg => name![neg],
}; };
let ty = self.ty_of_expr(db, &prefix_expr.expr()?.into())?; let ty = self.ty_of_expr(db, &prefix_expr.expr()?)?;
let (op_trait, op_fn) = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?; let (op_trait, op_fn) = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?;
// HACK: subst for all methods coincides with that for their trait because the methods // HACK: subst for all methods coincides with that for their trait because the methods
@ -331,8 +331,8 @@ impl SourceAnalyzer {
db: &dyn HirDatabase, db: &dyn HirDatabase,
index_expr: &ast::IndexExpr, index_expr: &ast::IndexExpr,
) -> Option<FunctionId> { ) -> Option<FunctionId> {
let base_ty = self.ty_of_expr(db, &index_expr.base()?.into())?; let base_ty = self.ty_of_expr(db, &index_expr.base()?)?;
let index_ty = self.ty_of_expr(db, &index_expr.index()?.into())?; let index_ty = self.ty_of_expr(db, &index_expr.index()?)?;
let lang_item_name = name![index]; let lang_item_name = name![index];
@ -352,8 +352,8 @@ impl SourceAnalyzer {
binop_expr: &ast::BinExpr, binop_expr: &ast::BinExpr,
) -> Option<FunctionId> { ) -> Option<FunctionId> {
let op = binop_expr.op_kind()?; let op = binop_expr.op_kind()?;
let lhs = self.ty_of_expr(db, &binop_expr.lhs()?.into())?; let lhs = self.ty_of_expr(db, &binop_expr.lhs()?)?;
let rhs = self.ty_of_expr(db, &binop_expr.rhs()?.into())?; let rhs = self.ty_of_expr(db, &binop_expr.rhs()?)?;
let (op_trait, op_fn) = lang_names_for_bin_op(op) let (op_trait, op_fn) = lang_names_for_bin_op(op)
.and_then(|(name, lang_item)| self.lang_trait_fn(db, &lang_item, &name))?; .and_then(|(name, lang_item)| self.lang_trait_fn(db, &lang_item, &name))?;
@ -372,7 +372,7 @@ impl SourceAnalyzer {
db: &dyn HirDatabase, db: &dyn HirDatabase,
try_expr: &ast::TryExpr, try_expr: &ast::TryExpr,
) -> Option<FunctionId> { ) -> Option<FunctionId> {
let ty = self.ty_of_expr(db, &try_expr.expr()?.into())?; let ty = self.ty_of_expr(db, &try_expr.expr()?)?;
let op_fn = let op_fn =
db.lang_item(self.resolver.krate(), name![branch].to_smol_str())?.as_function()?; db.lang_item(self.resolver.krate(), name![branch].to_smol_str())?.as_function()?;
@ -482,7 +482,7 @@ impl SourceAnalyzer {
let infer = self.infer.as_deref()?; let infer = self.infer.as_deref()?;
if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) { if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
let expr_id = self.expr_id(db, &path_expr.into())?; let expr_id = self.expr_id(db, &path_expr.into())?;
if let Some(assoc) = infer.assoc_resolutions_for_expr(expr_id) { if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr(expr_id) {
let assoc = match assoc { let assoc = match assoc {
AssocItemId::FunctionId(f_in_trait) => { AssocItemId::FunctionId(f_in_trait) => {
match infer.type_of_expr.get(expr_id) { match infer.type_of_expr.get(expr_id) {
@ -501,7 +501,9 @@ impl SourceAnalyzer {
} }
} }
} }
AssocItemId::ConstId(const_id) => {
self.resolve_impl_const_or_trait_def(db, const_id, subs).into()
}
_ => assoc, _ => assoc,
}; };
@ -515,7 +517,7 @@ impl SourceAnalyzer {
prefer_value_ns = true; prefer_value_ns = true;
} else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) { } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
let pat_id = self.pat_id(&path_pat.into())?; let pat_id = self.pat_id(&path_pat.into())?;
if let Some(assoc) = infer.assoc_resolutions_for_pat(pat_id) { if let Some((assoc, _)) = infer.assoc_resolutions_for_pat(pat_id) {
return Some(PathResolution::Def(AssocItem::from(assoc).into())); return Some(PathResolution::Def(AssocItem::from(assoc).into()));
} }
if let Some(VariantId::EnumVariantId(variant)) = if let Some(VariantId::EnumVariantId(variant)) =
@ -792,6 +794,24 @@ impl SourceAnalyzer {
method_resolution::lookup_impl_method(db, env, func, substs) method_resolution::lookup_impl_method(db, env, func, substs)
} }
fn resolve_impl_const_or_trait_def(
&self,
db: &dyn HirDatabase,
const_id: ConstId,
subs: Substitution,
) -> ConstId {
let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return const_id,
};
let env = owner.as_generic_def_id().map_or_else(
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|d| db.trait_environment(d),
);
method_resolution::lookup_impl_const(db, env, const_id, subs)
}
fn lang_trait_fn( fn lang_trait_fn(
&self, &self,
db: &dyn HirDatabase, db: &dyn HirDatabase,
@ -804,7 +824,7 @@ impl SourceAnalyzer {
} }
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> { fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
self.infer.as_ref()?.type_of_expr.get(self.expr_id(db, &expr)?) self.infer.as_ref()?.type_of_expr.get(self.expr_id(db, expr)?)
} }
} }
@ -967,7 +987,7 @@ fn resolve_hir_path_(
db, db,
def, def,
res.in_type_ns()?, res.in_type_ns()?,
|name, _, id| (name == unresolved.name).then(|| id), |name, id| (name == unresolved.name).then_some(id),
) )
}) })
.map(TypeAlias::from) .map(TypeAlias::from)

View file

@ -14,6 +14,7 @@ cov-mark = "2.0.0-pre.1"
itertools = "0.10.5" itertools = "0.10.5"
either = "1.7.0" either = "1.7.0"
smallvec = "1.10.0"
stdx = { path = "../stdx", version = "0.0.0" } stdx = { path = "../stdx", version = "0.0.0" }
syntax = { path = "../syntax", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" }

View file

@ -47,7 +47,10 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
// Don't enable the assist if there is a type ascription without any placeholders // Don't enable the assist if there is a type ascription without any placeholders
if let Some(ty) = &ascribed_ty { if let Some(ty) = &ascribed_ty {
let mut contains_infer_ty = false; let mut contains_infer_ty = false;
walk_ty(ty, &mut |ty| contains_infer_ty |= matches!(ty, ast::Type::InferType(_))); walk_ty(ty, &mut |ty| {
contains_infer_ty |= matches!(ty, ast::Type::InferType(_));
false
});
if !contains_infer_ty { if !contains_infer_ty {
cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified); cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
return None; return None;

View file

@ -107,6 +107,14 @@ fn add_missing_impl_members_inner(
) -> Option<()> { ) -> Option<()> {
let _p = profile::span("add_missing_impl_members_inner"); let _p = profile::span("add_missing_impl_members_inner");
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
if ctx.token_at_offset().all(|t| {
t.parent_ancestors()
.any(|s| ast::BlockExpr::can_cast(s.kind()) || ast::ParamList::can_cast(s.kind()))
}) {
return None;
}
let target_scope = ctx.sema.scope(impl_def.syntax())?; let target_scope = ctx.sema.scope(impl_def.syntax())?;
let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
@ -1343,4 +1351,95 @@ impl PartialEq for SomeStruct {
"#, "#,
); );
} }
#[test]
fn test_ignore_function_body() {
check_assist_not_applicable(
add_missing_default_members,
r#"
trait Trait {
type X;
fn foo(&self);
fn bar(&self) {}
}
impl Trait for () {
type X = u8;
fn foo(&self) {$0
let x = 5;
}
}"#,
)
}
#[test]
fn test_ignore_param_list() {
check_assist_not_applicable(
add_missing_impl_members,
r#"
trait Trait {
type X;
fn foo(&self);
fn bar(&self);
}
impl Trait for () {
type X = u8;
fn foo(&self$0) {
let x = 5;
}
}"#,
)
}
#[test]
fn test_ignore_scope_inside_function() {
check_assist_not_applicable(
add_missing_impl_members,
r#"
trait Trait {
type X;
fn foo(&self);
fn bar(&self);
}
impl Trait for () {
type X = u8;
fn foo(&self) {
let x = async {$0 5 };
}
}"#,
)
}
#[test]
fn test_apply_outside_function() {
check_assist(
add_missing_default_members,
r#"
trait Trait {
type X;
fn foo(&self);
fn bar(&self) {}
}
impl Trait for () {
type X = u8;
fn foo(&self)$0 {}
}"#,
r#"
trait Trait {
type X;
fn foo(&self);
fn bar(&self) {}
}
impl Trait for () {
type X = u8;
fn foo(&self) {}
$0fn bar(&self) {}
}"#,
)
}
} }

View file

@ -326,7 +326,7 @@ impl ExtendedEnum {
fn resolve_enum_def(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { fn resolve_enum_def(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
sema.type_of_expr(expr)?.adjusted().autoderef(sema.db).find_map(|ty| match ty.as_adt() { sema.type_of_expr(expr)?.adjusted().autoderef(sema.db).find_map(|ty| match ty.as_adt() {
Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
_ => ty.is_bool().then(|| ExtendedEnum::Bool), _ => ty.is_bool().then_some(ExtendedEnum::Bool),
}) })
} }
@ -344,7 +344,7 @@ fn resolve_tuple_of_enum_def(
// For now we only handle expansion for a tuple of enums. Here // For now we only handle expansion for a tuple of enums. Here
// we map non-enum items to None and rely on `collect` to // we map non-enum items to None and rely on `collect` to
// convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
_ => ty.is_bool().then(|| ExtendedEnum::Bool), _ => ty.is_bool().then_some(ExtendedEnum::Bool),
}) })
}) })
.collect() .collect()

View file

@ -35,16 +35,16 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
match builder_edit_pos { match builder_edit_pos {
InsertOrReplace::Insert(insert_pos, needs_whitespace) => { InsertOrReplace::Insert(insert_pos, needs_whitespace) => {
let preceeding_whitespace = if needs_whitespace { " " } else { "" }; let preceeding_whitespace = if needs_whitespace { " " } else { "" };
builder.insert(insert_pos, &format!("{preceeding_whitespace}-> {ty} ")) builder.insert(insert_pos, format!("{preceeding_whitespace}-> {ty} "))
} }
InsertOrReplace::Replace(text_range) => { InsertOrReplace::Replace(text_range) => {
builder.replace(text_range, &format!("-> {ty}")) builder.replace(text_range, format!("-> {ty}"))
} }
} }
if let FnType::Closure { wrap_expr: true } = fn_type { if let FnType::Closure { wrap_expr: true } = fn_type {
cov_mark::hit!(wrap_closure_non_block_expr); cov_mark::hit!(wrap_closure_non_block_expr);
// `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
builder.replace(tail_expr.syntax().text_range(), &format!("{{{tail_expr}}}")); builder.replace(tail_expr.syntax().text_range(), format!("{{{tail_expr}}}"));
} }
}, },
) )

View file

@ -203,7 +203,7 @@ fn relevance_score(
// get the distance between the imported path and the current module // get the distance between the imported path and the current module
// (prefer items that are more local) // (prefer items that are more local)
Some((item_module, current_module)) => { Some((item_module, current_module)) => {
score -= module_distance_hueristic(db, &current_module, &item_module) as i32; score -= module_distance_hueristic(db, current_module, &item_module) as i32;
} }
// could not find relevant modules, so just use the length of the path as an estimate // could not find relevant modules, so just use the length of the path as an estimate

View file

@ -216,7 +216,7 @@ fn validate_method_call_expr(
let krate = module.krate(); let krate = module.krate();
let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?; let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver)) it_type.impls_trait(sema.db, iter_trait, &[]).then_some((expr, receiver))
} }
#[cfg(test)] #[cfg(test)]

View file

@ -168,7 +168,7 @@ fn edit_struct_references(
let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?; let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?;
edit.replace( edit.replace(
call_expr.syntax().text_range(), ctx.sema.original_range(&node).range,
ast::make::record_expr( ast::make::record_expr(
path, path,
ast::make::record_expr_field_list(arg_list.args().zip(names).map( ast::make::record_expr_field_list(arg_list.args().zip(names).map(
@ -249,6 +249,24 @@ mod tests {
); );
check_assist_not_applicable(convert_tuple_struct_to_named_struct, r#"struct Foo$0;"#); check_assist_not_applicable(convert_tuple_struct_to_named_struct, r#"struct Foo$0;"#);
} }
#[test]
fn convert_in_macro_args() {
check_assist(
convert_tuple_struct_to_named_struct,
r#"
macro_rules! foo {($i:expr) => {$i} }
struct T$0(u8);
fn test() {
foo!(T(1));
}"#,
r#"
macro_rules! foo {($i:expr) => {$i} }
struct T { field1: u8 }
fn test() {
foo!(T { field1: 1 });
}"#,
);
}
#[test] #[test]
fn convert_simple_struct() { fn convert_simple_struct() {
@ -554,6 +572,29 @@ where
); );
} }
#[test]
fn convert_variant_in_macro_args() {
check_assist(
convert_tuple_struct_to_named_struct,
r#"
macro_rules! foo {($i:expr) => {$i} }
enum T {
V$0(u8)
}
fn test() {
foo!(T::V(1));
}"#,
r#"
macro_rules! foo {($i:expr) => {$i} }
enum T {
V { field1: u8 }
}
fn test() {
foo!(T::V { field1: 1 });
}"#,
);
}
#[test] #[test]
fn convert_simple_variant() { fn convert_simple_variant() {
check_assist( check_assist(

View file

@ -10,7 +10,7 @@ use itertools::Itertools;
use stdx::format_to; use stdx::format_to;
use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange}; use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
// Assist: move_format_string_arg // Assist: extract_expressions_from_format_string
// //
// Move an expression out of a format string. // Move an expression out of a format string.
// //
@ -23,7 +23,7 @@ use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
// } // }
// //
// fn main() { // fn main() {
// print!("{x + 1}$0"); // print!("{var} {x + 1}$0");
// } // }
// ``` // ```
// -> // ->
@ -36,11 +36,14 @@ use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
// } // }
// //
// fn main() { // fn main() {
// print!("{}"$0, x + 1); // print!("{var} {}"$0, x + 1);
// } // }
// ``` // ```
pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { pub(crate) fn extract_expressions_from_format_string(
acc: &mut Assists,
ctx: &AssistContext<'_>,
) -> Option<()> {
let fmt_string = ctx.find_token_at_offset::<ast::String>()?; let fmt_string = ctx.find_token_at_offset::<ast::String>()?;
let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?; let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
@ -58,7 +61,7 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
acc.add( acc.add(
AssistId( AssistId(
"move_format_string_arg", "extract_expressions_from_format_string",
// if there aren't any expressions, then make the assist a RefactorExtract // if there aren't any expressions, then make the assist a RefactorExtract
if extracted_args.iter().filter(|f| matches!(f, Arg::Expr(_))).count() == 0 { if extracted_args.iter().filter(|f| matches!(f, Arg::Expr(_))).count() == 0 {
AssistKind::RefactorExtract AssistKind::RefactorExtract
@ -66,7 +69,7 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
AssistKind::QuickFix AssistKind::QuickFix
}, },
), ),
"Extract format args", "Extract format expressions",
tt.syntax().text_range(), tt.syntax().text_range(),
|edit| { |edit| {
let fmt_range = fmt_string.syntax().text_range(); let fmt_range = fmt_string.syntax().text_range();
@ -118,15 +121,14 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
let mut placeholder_idx = 1; let mut placeholder_idx = 1;
for extracted_args in extracted_args { for extracted_args in extracted_args {
// remove expr from format string
args.push_str(", ");
match extracted_args { match extracted_args {
Arg::Ident(s) | Arg::Expr(s) => { Arg::Expr(s)=> {
args.push_str(", ");
// insert arg // insert arg
args.push_str(&s); args.push_str(&s);
} }
Arg::Placeholder => { Arg::Placeholder => {
args.push_str(", ");
// try matching with existing argument // try matching with existing argument
match existing_args.next() { match existing_args.next() {
Some(ea) => { Some(ea) => {
@ -139,6 +141,7 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
} }
} }
} }
Arg::Ident(_s) => (),
} }
} }
@ -171,7 +174,7 @@ macro_rules! print {
#[test] #[test]
fn multiple_middle_arg() { fn multiple_middle_arg() {
check_assist( check_assist(
move_format_string_arg, extract_expressions_from_format_string,
&add_macro_decl( &add_macro_decl(
r#" r#"
fn main() { fn main() {
@ -192,7 +195,7 @@ fn main() {
#[test] #[test]
fn single_arg() { fn single_arg() {
check_assist( check_assist(
move_format_string_arg, extract_expressions_from_format_string,
&add_macro_decl( &add_macro_decl(
r#" r#"
fn main() { fn main() {
@ -213,7 +216,7 @@ fn main() {
#[test] #[test]
fn multiple_middle_placeholders_arg() { fn multiple_middle_placeholders_arg() {
check_assist( check_assist(
move_format_string_arg, extract_expressions_from_format_string,
&add_macro_decl( &add_macro_decl(
r#" r#"
fn main() { fn main() {
@ -234,7 +237,7 @@ fn main() {
#[test] #[test]
fn multiple_trailing_args() { fn multiple_trailing_args() {
check_assist( check_assist(
move_format_string_arg, extract_expressions_from_format_string,
&add_macro_decl( &add_macro_decl(
r#" r#"
fn main() { fn main() {
@ -255,7 +258,7 @@ fn main() {
#[test] #[test]
fn improper_commas() { fn improper_commas() {
check_assist( check_assist(
move_format_string_arg, extract_expressions_from_format_string,
&add_macro_decl( &add_macro_decl(
r#" r#"
fn main() { fn main() {
@ -276,7 +279,7 @@ fn main() {
#[test] #[test]
fn nested_tt() { fn nested_tt() {
check_assist( check_assist(
move_format_string_arg, extract_expressions_from_format_string,
&add_macro_decl( &add_macro_decl(
r#" r#"
fn main() { fn main() {
@ -289,6 +292,29 @@ fn main() {
fn main() { fn main() {
print!("My name is {} {}"$0, stringify!(Paperino), x + x) print!("My name is {} {}"$0, stringify!(Paperino), x + x)
} }
"#,
),
);
}
#[test]
fn extract_only_expressions() {
check_assist(
extract_expressions_from_format_string,
&add_macro_decl(
r#"
fn main() {
let var = 1 + 1;
print!("foobar {var} {var:?} {x$0 + x}")
}
"#,
),
&add_macro_decl(
r#"
fn main() {
let var = 1 + 1;
print!("foobar {var} {var:?} {}"$0, x + x)
}
"#, "#,
), ),
); );

View file

@ -11,7 +11,9 @@ use ide_db::{
helpers::mod_path_to_ast, helpers::mod_path_to_ast,
imports::insert_use::{insert_use, ImportScope}, imports::insert_use::{insert_use, ImportScope},
search::{FileReference, ReferenceCategory, SearchScope}, search::{FileReference, ReferenceCategory, SearchScope},
syntax_helpers::node_ext::{preorder_expr, walk_expr, walk_pat, walk_patterns_in_expr}, syntax_helpers::node_ext::{
for_each_tail_expr, preorder_expr, walk_expr, walk_pat, walk_patterns_in_expr,
},
FxIndexSet, RootDatabase, FxIndexSet, RootDatabase,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -78,7 +80,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
}; };
let body = extraction_target(&node, range)?; let body = extraction_target(&node, range)?;
let container_info = body.analyze_container(&ctx.sema)?; let (container_info, contains_tail_expr) = body.analyze_container(&ctx.sema)?;
let (locals_used, self_param) = body.analyze(&ctx.sema); let (locals_used, self_param) = body.analyze(&ctx.sema);
@ -119,6 +121,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
ret_ty, ret_ty,
body, body,
outliving_locals, outliving_locals,
contains_tail_expr,
mods: container_info, mods: container_info,
}; };
@ -245,6 +248,8 @@ struct Function {
ret_ty: RetType, ret_ty: RetType,
body: FunctionBody, body: FunctionBody,
outliving_locals: Vec<OutlivedLocal>, outliving_locals: Vec<OutlivedLocal>,
/// Whether at least one of the container's tail expr is contained in the range we're extracting.
contains_tail_expr: bool,
mods: ContainerInfo, mods: ContainerInfo,
} }
@ -265,7 +270,7 @@ enum ParamKind {
MutRef, MutRef,
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Debug)]
enum FunType { enum FunType {
Unit, Unit,
Single(hir::Type), Single(hir::Type),
@ -294,7 +299,6 @@ struct ControlFlow {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct ContainerInfo { struct ContainerInfo {
is_const: bool, is_const: bool,
is_in_tail: bool,
parent_loop: Option<SyntaxNode>, parent_loop: Option<SyntaxNode>,
/// The function's return type, const's type etc. /// The function's return type, const's type etc.
ret_type: Option<hir::Type>, ret_type: Option<hir::Type>,
@ -584,7 +588,7 @@ impl FunctionBody {
FunctionBody::Expr(expr) => Some(expr.clone()), FunctionBody::Expr(expr) => Some(expr.clone()),
FunctionBody::Span { parent, text_range } => { FunctionBody::Span { parent, text_range } => {
let tail_expr = parent.tail_expr()?; let tail_expr = parent.tail_expr()?;
text_range.contains_range(tail_expr.syntax().text_range()).then(|| tail_expr) text_range.contains_range(tail_expr.syntax().text_range()).then_some(tail_expr)
} }
} }
} }
@ -743,7 +747,10 @@ impl FunctionBody {
(res, self_param) (res, self_param)
} }
fn analyze_container(&self, sema: &Semantics<'_, RootDatabase>) -> Option<ContainerInfo> { fn analyze_container(
&self,
sema: &Semantics<'_, RootDatabase>,
) -> Option<(ContainerInfo, bool)> {
let mut ancestors = self.parent()?.ancestors(); let mut ancestors = self.parent()?.ancestors();
let infer_expr_opt = |expr| sema.type_of_expr(&expr?).map(TypeInfo::adjusted); let infer_expr_opt = |expr| sema.type_of_expr(&expr?).map(TypeInfo::adjusted);
let mut parent_loop = None; let mut parent_loop = None;
@ -815,28 +822,36 @@ impl FunctionBody {
} }
}; };
}; };
let container_tail = match expr? {
ast::Expr::BlockExpr(block) => block.tail_expr(), let expr = expr?;
expr => Some(expr), let contains_tail_expr = if let Some(body_tail) = self.tail_expr() {
}; let mut contains_tail_expr = false;
let is_in_tail = let tail_expr_range = body_tail.syntax().text_range();
container_tail.zip(self.tail_expr()).map_or(false, |(container_tail, body_tail)| { for_each_tail_expr(&expr, &mut |e| {
container_tail.syntax().text_range().contains_range(body_tail.syntax().text_range()) if tail_expr_range.contains_range(e.syntax().text_range()) {
contains_tail_expr = true;
}
}); });
contains_tail_expr
} else {
false
};
let parent = self.parent()?; let parent = self.parent()?;
let parents = generic_parents(&parent); let parents = generic_parents(&parent);
let generic_param_lists = parents.iter().filter_map(|it| it.generic_param_list()).collect(); let generic_param_lists = parents.iter().filter_map(|it| it.generic_param_list()).collect();
let where_clauses = parents.iter().filter_map(|it| it.where_clause()).collect(); let where_clauses = parents.iter().filter_map(|it| it.where_clause()).collect();
Some(ContainerInfo { Some((
is_in_tail, ContainerInfo {
is_const, is_const,
parent_loop, parent_loop,
ret_type: ty, ret_type: ty,
generic_param_lists, generic_param_lists,
where_clauses, where_clauses,
}) },
contains_tail_expr,
))
} }
fn return_ty(&self, ctx: &AssistContext<'_>) -> Option<RetType> { fn return_ty(&self, ctx: &AssistContext<'_>) -> Option<RetType> {
@ -1368,7 +1383,7 @@ impl FlowHandler {
None => FlowHandler::None, None => FlowHandler::None,
Some(flow_kind) => { Some(flow_kind) => {
let action = flow_kind.clone(); let action = flow_kind.clone();
if *ret_ty == FunType::Unit { if let FunType::Unit = ret_ty {
match flow_kind { match flow_kind {
FlowKind::Return(None) FlowKind::Return(None)
| FlowKind::Break(_, None) | FlowKind::Break(_, None)
@ -1633,7 +1648,7 @@ impl Function {
fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> { fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
let fun_ty = self.return_type(ctx); let fun_ty = self.return_type(ctx);
let handler = if self.mods.is_in_tail { let handler = if self.contains_tail_expr {
FlowHandler::None FlowHandler::None
} else { } else {
FlowHandler::from_ret_ty(self, &fun_ty) FlowHandler::from_ret_ty(self, &fun_ty)
@ -1707,7 +1722,7 @@ fn make_body(
fun: &Function, fun: &Function,
) -> ast::BlockExpr { ) -> ast::BlockExpr {
let ret_ty = fun.return_type(ctx); let ret_ty = fun.return_type(ctx);
let handler = if fun.mods.is_in_tail { let handler = if fun.contains_tail_expr {
FlowHandler::None FlowHandler::None
} else { } else {
FlowHandler::from_ret_ty(fun, &ret_ty) FlowHandler::from_ret_ty(fun, &ret_ty)
@ -1785,7 +1800,7 @@ fn make_body(
.collect::<Vec<SyntaxElement>>(); .collect::<Vec<SyntaxElement>>();
let tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent)); let tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent));
make::hacky_block_expr_with_comments(elements, tail_expr) make::hacky_block_expr(elements, tail_expr)
} }
}; };
@ -1845,9 +1860,29 @@ fn with_default_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::B
} }
fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr { fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr {
let stmt_tail = block.tail_expr().map(|expr| make::expr_stmt(expr).into()); let stmt_tail_opt: Option<ast::Stmt> =
let stmts = block.statements().chain(stmt_tail); block.tail_expr().map(|expr| make::expr_stmt(expr).into());
make::block_expr(stmts, Some(tail_expr))
let mut elements: Vec<SyntaxElement> = vec![];
block.statements().for_each(|stmt| {
elements.push(syntax::NodeOrToken::Node(stmt.syntax().clone()));
});
if let Some(stmt_list) = block.stmt_list() {
stmt_list.syntax().children_with_tokens().for_each(|node_or_token| {
match &node_or_token {
syntax::NodeOrToken::Token(_) => elements.push(node_or_token),
_ => (),
};
});
}
if let Some(stmt_tail) = stmt_tail_opt {
elements.push(syntax::NodeOrToken::Node(stmt_tail.syntax().clone()));
}
make::hacky_block_expr(elements, Some(tail_expr))
} }
fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String { fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
@ -1946,7 +1981,7 @@ fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) {
if nested_scope.is_none() { if nested_scope.is_none() {
if let Some(expr) = ast::Expr::cast(e.clone()) { if let Some(expr) = ast::Expr::cast(e.clone()) {
match expr { match expr {
ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => { ast::Expr::ReturnExpr(return_expr) => {
let expr = return_expr.expr(); let expr = return_expr.expr();
if let Some(replacement) = make_rewritten_flow(handler, expr) { if let Some(replacement) = make_rewritten_flow(handler, expr) {
ted::replace(return_expr.syntax(), replacement.syntax()) ted::replace(return_expr.syntax(), replacement.syntax())
@ -4944,9 +4979,8 @@ fn $0fun_name() {
); );
} }
// FIXME: we do want to preserve whitespace
#[test] #[test]
fn extract_function_does_not_preserve_whitespace() { fn extract_function_does_preserve_whitespace() {
check_assist( check_assist(
extract_function, extract_function,
r#" r#"
@ -4965,6 +4999,7 @@ fn func() {
fn $0fun_name() { fn $0fun_name() {
let a = 0; let a = 0;
let x = 0; let x = 0;
} }
"#, "#,
@ -5582,6 +5617,193 @@ impl <T, U> Struct<T, U> where T: Into<i32> + Copy, U: Debug {
fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> { fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
t.into() + v.into() t.into() + v.into()
} }
"#,
);
}
#[test]
fn non_tail_expr_of_tail_expr_loop() {
check_assist(
extract_function,
r#"
pub fn f() {
loop {
$0if true {
continue;
}$0
if false {
break;
}
}
}
"#,
r#"
pub fn f() {
loop {
if let ControlFlow::Break(_) = fun_name() {
continue;
}
if false {
break;
}
}
}
fn $0fun_name() -> ControlFlow<()> {
if true {
return ControlFlow::Break(());
}
ControlFlow::Continue(())
}
"#,
);
}
#[test]
fn non_tail_expr_of_tail_if_block() {
// FIXME: double semicolon
check_assist(
extract_function,
r#"
//- minicore: option, try
impl<T> core::ops::Try for Option<T> {
type Output = T;
type Residual = Option<!>;
}
impl<T> core::ops::FromResidual for Option<T> {}
fn f() -> Option<()> {
if true {
let a = $0if true {
Some(())?
} else {
()
}$0;
Some(a)
} else {
None
}
}
"#,
r#"
impl<T> core::ops::Try for Option<T> {
type Output = T;
type Residual = Option<!>;
}
impl<T> core::ops::FromResidual for Option<T> {}
fn f() -> Option<()> {
if true {
let a = fun_name()?;;
Some(a)
} else {
None
}
}
fn $0fun_name() -> Option<()> {
Some(if true {
Some(())?
} else {
()
})
}
"#,
);
}
#[test]
fn tail_expr_of_tail_block_nested() {
check_assist(
extract_function,
r#"
//- minicore: option, try
impl<T> core::ops::Try for Option<T> {
type Output = T;
type Residual = Option<!>;
}
impl<T> core::ops::FromResidual for Option<T> {}
fn f() -> Option<()> {
if true {
$0{
let a = if true {
Some(())?
} else {
()
};
Some(a)
}$0
} else {
None
}
}
"#,
r#"
impl<T> core::ops::Try for Option<T> {
type Output = T;
type Residual = Option<!>;
}
impl<T> core::ops::FromResidual for Option<T> {}
fn f() -> Option<()> {
if true {
fun_name()?
} else {
None
}
}
fn $0fun_name() -> Option<()> {
let a = if true {
Some(())?
} else {
()
};
Some(a)
}
"#,
);
}
#[test]
fn non_tail_expr_with_comment_of_tail_expr_loop() {
check_assist(
extract_function,
r#"
pub fn f() {
loop {
$0// A comment
if true {
continue;
}$0
if false {
break;
}
}
}
"#,
r#"
pub fn f() {
loop {
if let ControlFlow::Break(_) = fun_name() {
continue;
}
if false {
break;
}
}
}
fn $0fun_name() -> ControlFlow<()> {
// A comment
if true {
return ControlFlow::Break(());
}
ControlFlow::Continue(())
}
"#, "#,
); );
} }

View file

@ -10,6 +10,8 @@ use ide_db::{
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
search::{FileReference, SearchScope}, search::{FileReference, SearchScope},
}; };
use itertools::Itertools;
use smallvec::SmallVec;
use stdx::format_to; use stdx::format_to;
use syntax::{ use syntax::{
algo::find_node_at_range, algo::find_node_at_range,
@ -116,13 +118,13 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
let mut body_items: Vec<String> = Vec::new(); let mut body_items: Vec<String> = Vec::new();
let mut items_to_be_processed: Vec<ast::Item> = module.body_items.clone(); let mut items_to_be_processed: Vec<ast::Item> = module.body_items.clone();
let mut new_item_indent = old_item_indent + 1;
if impl_parent.is_some() { let new_item_indent = if impl_parent.is_some() {
new_item_indent = old_item_indent + 2; old_item_indent + 2
} else { } else {
items_to_be_processed = [module.use_items.clone(), items_to_be_processed].concat(); items_to_be_processed = [module.use_items.clone(), items_to_be_processed].concat();
} old_item_indent + 1
};
for item in items_to_be_processed { for item in items_to_be_processed {
let item = item.indent(IndentLevel(1)); let item = item.indent(IndentLevel(1));
@ -657,28 +659,23 @@ impl Module {
fn check_intersection_and_push( fn check_intersection_and_push(
import_paths_to_be_removed: &mut Vec<TextRange>, import_paths_to_be_removed: &mut Vec<TextRange>,
import_path: TextRange, mut import_path: TextRange,
) { ) {
if import_paths_to_be_removed.len() > 0 { // Text ranges received here for imports are extended to the
// Text ranges received here for imports are extended to the // next/previous comma which can cause intersections among them
// next/previous comma which can cause intersections among them // and later deletion of these can cause panics similar
// and later deletion of these can cause panics similar // to reported in #11766. So to mitigate it, we
// to reported in #11766. So to mitigate it, we // check for intersection between all current members
// check for intersection between all current members // and combine all such ranges into one.
// and if it exists we combine both text ranges into let s: SmallVec<[_; 2]> = import_paths_to_be_removed
// one .into_iter()
let r = import_paths_to_be_removed .positions(|it| it.intersect(import_path).is_some())
.into_iter() .collect();
.position(|it| it.intersect(import_path).is_some()); for pos in s.into_iter().rev() {
match r { let intersecting_path = import_paths_to_be_removed.swap_remove(pos);
Some(it) => { import_path = import_path.cover(intersecting_path);
import_paths_to_be_removed[it] = import_paths_to_be_removed[it].cover(import_path)
}
None => import_paths_to_be_removed.push(import_path),
}
} else {
import_paths_to_be_removed.push(import_path);
} }
import_paths_to_be_removed.push(import_path);
} }
fn does_source_exists_outside_sel_in_same_mod( fn does_source_exists_outside_sel_in_same_mod(
@ -1766,4 +1763,49 @@ mod modname {
", ",
) )
} }
#[test]
fn test_merge_multiple_intersections() {
check_assist(
extract_module,
r#"
mod dep {
pub struct A;
pub struct B;
pub struct C;
}
use dep::{A, B, C};
$0struct S {
inner: A,
state: C,
condvar: B,
}$0
"#,
r#"
mod dep {
pub struct A;
pub struct B;
pub struct C;
}
use dep::{};
mod modname {
use super::dep::B;
use super::dep::C;
use super::dep::A;
pub(crate) struct S {
pub(crate) inner: A,
pub(crate) state: C,
pub(crate) condvar: B,
}
}
"#,
);
}
} }

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