mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Merge branch 'master' of github.com:rust-analyzer/rust-analyzer into fix_4202
This commit is contained in:
commit
4613497a77
313 changed files with 13659 additions and 11704 deletions
1
.github/workflows/release.yaml
vendored
1
.github/workflows/release.yaml
vendored
|
@ -39,7 +39,6 @@ jobs:
|
|||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
target: x86_64-unknown-linux-musl
|
||||
override: true
|
||||
|
||||
- name: Install Nodejs
|
||||
|
|
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
@ -41,7 +41,7 @@
|
|||
"outFiles": [
|
||||
"${workspaceFolder}/editors/code/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "Build Extension",
|
||||
"preLaunchTask": "Build Server and Extension",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**/*.js"
|
||||
],
|
||||
|
@ -62,7 +62,7 @@
|
|||
"outFiles": [
|
||||
"${workspaceFolder}/editors/code/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "Build Extension",
|
||||
"preLaunchTask": "Build Server (Release) and Extension",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**/*.js"
|
||||
],
|
||||
|
|
31
.vscode/tasks.json
vendored
31
.vscode/tasks.json
vendored
|
@ -4,7 +4,7 @@
|
|||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build Extension",
|
||||
"label": "Build Extension in Background",
|
||||
"group": "build",
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
|
@ -15,6 +15,17 @@
|
|||
},
|
||||
"isBackground": true,
|
||||
},
|
||||
{
|
||||
"label": "Build Extension",
|
||||
"group": "build",
|
||||
"type": "npm",
|
||||
"script": "build",
|
||||
"path": "editors/code/",
|
||||
"problemMatcher": {
|
||||
"base": "$tsc",
|
||||
"fileLocation": ["relative", "${workspaceFolder}/editors/code/"]
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "Build Server",
|
||||
"group": "build",
|
||||
|
@ -22,5 +33,23 @@
|
|||
"command": "cargo build --package rust-analyzer",
|
||||
"problemMatcher": "$rustc"
|
||||
},
|
||||
{
|
||||
"label": "Build Server (Release)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "cargo build --release --package rust-analyzer",
|
||||
"problemMatcher": "$rustc"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "Build Server and Extension",
|
||||
"dependsOn": ["Build Server", "Build Extension"],
|
||||
"problemMatcher": "$rustc"
|
||||
},
|
||||
{
|
||||
"label": "Build Server (Release) and Extension",
|
||||
"dependsOn": ["Build Server (Release)", "Build Extension"],
|
||||
"problemMatcher": "$rustc"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
88
Cargo.lock
generated
88
Cargo.lock
generated
|
@ -58,9 +58,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "backtrace-sys"
|
||||
version = "0.1.36"
|
||||
version = "0.1.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78848718ee1255a2485d1309ad9cdecfc2e7d0362dd11c6829364c6b35ae1bc7"
|
||||
checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
@ -68,9 +68,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.11.0"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
||||
checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
|
@ -170,7 +170,7 @@ dependencies = [
|
|||
"chalk-ir",
|
||||
"chalk-macros",
|
||||
"chalk-rust-ir",
|
||||
"ena",
|
||||
"ena 0.13.1",
|
||||
"itertools",
|
||||
"petgraph",
|
||||
"rustc-hash",
|
||||
|
@ -199,14 +199,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.10.0"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6728a28023f207181b193262711102bfbaf47cc9d13bc71d0736607ef8efe88c"
|
||||
checksum = "2586208b33573b7f76ccfbe5adb076394c88deaf81b84d7213969805b0a952a7"
|
||||
dependencies = [
|
||||
"clicolors-control",
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"terminal_size",
|
||||
"termios",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
@ -315,6 +316,15 @@ dependencies = [
|
|||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
|
@ -381,9 +391,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fst"
|
||||
version = "0.4.1"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eaf9ea41cc964d742f7fc7861db75d2d6e83a3ce0d897d5c6f8b621f015ddc8"
|
||||
checksum = "81f9cac32c1741cdf6b66be7dcf0d9c7f25ccf12f8aa84c16cfa31f9f14513b3"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon"
|
||||
|
@ -447,9 +457,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15"
|
||||
checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -645,9 +655,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lsp-types"
|
||||
version = "0.73.0"
|
||||
version = "0.74.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d0cf64ea141b43d9e055f6b9df13f0bce32b103d84237509ce0a571ab9b159"
|
||||
checksum = "57c0e6a2b8837d27b29deb3f3e6dc1c6d2f57947677f9be1024e482ec5b59525"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags",
|
||||
|
@ -696,9 +706,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.21"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f"
|
||||
checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fuchsia-zircon",
|
||||
|
@ -739,9 +749,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.33"
|
||||
version = "0.2.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||
checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -814,9 +824,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab4fb1930692d1b6a9cfabdde3d06ea0a7d186518e2f4d67660d8970e2fa647a"
|
||||
checksum = "a3c897744f63f34f7ae3a024d9162bb5001f4ad661dd24bea0dc9f075d2de1c6"
|
||||
dependencies = [
|
||||
"paste-impl",
|
||||
"proc-macro-hack",
|
||||
|
@ -824,9 +834,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "paste-impl"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a62486e111e571b1e93b710b61e8f493c0013be39629b714cb166bdb06aa5a8a"
|
||||
checksum = "66fd6f92e3594f2dd7b3fc23e42d82e292f7bcda6d8e5dcd167072327234ab89"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
|
@ -885,9 +895,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
|
||||
checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -1023,7 +1033,7 @@ dependencies = [
|
|||
"chalk-ir",
|
||||
"chalk-rust-ir",
|
||||
"chalk-solve",
|
||||
"ena",
|
||||
"ena 0.14.0",
|
||||
"insta",
|
||||
"itertools",
|
||||
"log",
|
||||
|
@ -1193,9 +1203,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra_vfs"
|
||||
version = "0.5.3"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58a265769d5e5655345a9fcbd870a1a7c3658558c0d8efaed79e0669358f46b8"
|
||||
checksum = "fcaa5615f420134aea7667253db101d03a5c5f300eac607872dc2a36407b2ac9"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"jod-thread",
|
||||
|
@ -1374,9 +1384,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc-ap-rustc_lexer"
|
||||
version = "652.0.0"
|
||||
version = "656.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a6a43c4d0889218c5e2ae68ffea239f303fc05ab1078c73f74e63feb87f7889"
|
||||
checksum = "9cbba98ec46e96a4663197dfa8c0378752de2006e314e5400c0ca74929d6692f"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
@ -1486,18 +1496,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.104"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.104"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1506,9 +1516,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.51"
|
||||
version = "1.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9"
|
||||
checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -1606,6 +1616,16 @@ dependencies = [
|
|||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8038f95fc7a6f351163f4b964af631bd26c9e828f7db085f2a84aca56f70d13b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termios"
|
||||
version = "0.3.2"
|
||||
|
|
|
@ -85,6 +85,7 @@ impl<'a> SubstituteTypeParams<'a> {
|
|||
ast::TypeRef::PathType(path_type) => path_type.path()?,
|
||||
_ => return None,
|
||||
};
|
||||
// FIXME: use `hir::Path::from_src` instead.
|
||||
let path = hir::Path::from_ast(path)?;
|
||||
let resolution = self.source_scope.resolve_hir_path(&path)?;
|
||||
match resolution {
|
||||
|
@ -128,6 +129,7 @@ impl<'a> QualifyPaths<'a> {
|
|||
// don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
|
||||
return None;
|
||||
}
|
||||
// FIXME: use `hir::Path::from_src` instead.
|
||||
let hir_path = hir::Path::from_ast(p.clone());
|
||||
let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
|
||||
match resolution {
|
||||
|
|
|
@ -180,7 +180,9 @@ trait Trait<T> {
|
|||
}
|
||||
|
||||
impl Trait<u32> for () {
|
||||
fn foo(&self) -> u32 { todo!() }
|
||||
fn foo(&self) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
}
|
||||
"#####,
|
||||
|
@ -726,3 +728,22 @@ use std::{collections::HashMap};
|
|||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_unwrap_block() {
|
||||
check(
|
||||
"unwrap_block",
|
||||
r#####"
|
||||
fn foo() {
|
||||
if true {<|>
|
||||
println!("foo");
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn foo() {
|
||||
println!("foo");
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, NameOwner},
|
||||
TextSize,
|
||||
};
|
||||
use stdx::format_to;
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
use ra_ide_db::RootDatabase;
|
||||
use crate::{utils::FamousDefs, Assist, AssistCtx, AssistId};
|
||||
use test_utils::tested_by;
|
||||
|
||||
// Assist add_from_impl_for_enum
|
||||
//
|
||||
|
@ -41,7 +42,8 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> {
|
|||
_ => return None,
|
||||
};
|
||||
|
||||
if already_has_from_impl(ctx.sema, &variant) {
|
||||
if existing_from_impl(ctx.sema, &variant).is_some() {
|
||||
tested_by!(test_add_from_impl_already_exists);
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -70,41 +72,33 @@ impl From<{0}> for {1} {{
|
|||
)
|
||||
}
|
||||
|
||||
fn already_has_from_impl(
|
||||
fn existing_from_impl(
|
||||
sema: &'_ hir::Semantics<'_, RootDatabase>,
|
||||
variant: &ast::EnumVariant,
|
||||
) -> bool {
|
||||
let scope = sema.scope(&variant.syntax());
|
||||
) -> Option<()> {
|
||||
let variant = sema.to_def(variant)?;
|
||||
let enum_ = variant.parent_enum(sema.db);
|
||||
let krate = enum_.module(sema.db).krate();
|
||||
|
||||
let from_path = ast::make::path_from_text("From");
|
||||
let from_hir_path = match hir::Path::from_ast(from_path) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
let from_trait = match scope.resolve_hir_path(&from_hir_path) {
|
||||
Some(hir::PathResolution::Def(hir::ModuleDef::Trait(t))) => t,
|
||||
_ => return false,
|
||||
};
|
||||
let from_trait = FamousDefs(sema, krate).core_convert_From()?;
|
||||
|
||||
let e: hir::Enum = match sema.to_def(&variant.parent_enum()) {
|
||||
Some(e) => e,
|
||||
None => return false,
|
||||
};
|
||||
let e_ty = e.ty(sema.db);
|
||||
let enum_type = enum_.ty(sema.db);
|
||||
|
||||
let hir_enum_var: hir::EnumVariant = match sema.to_def(variant) {
|
||||
Some(ev) => ev,
|
||||
None => return false,
|
||||
};
|
||||
let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db);
|
||||
let wrapped_type = variant.fields(sema.db).get(0)?.signature_ty(sema.db);
|
||||
|
||||
e_ty.impls_trait(sema.db, from_trait, &[var_ty])
|
||||
if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::helpers::{check_assist, check_assist_not_applicable};
|
||||
use test_utils::covers;
|
||||
|
||||
#[test]
|
||||
fn test_add_from_impl_for_enum() {
|
||||
|
@ -136,36 +130,40 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
fn check_not_applicable(ra_fixture: &str) {
|
||||
let fixture =
|
||||
format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
|
||||
check_assist_not_applicable(add_from_impl_for_enum, &fixture)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_from_impl_no_element() {
|
||||
check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One }");
|
||||
check_not_applicable("enum A { <|>One }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_from_impl_more_than_one_element_in_tuple() {
|
||||
check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One(u32, String) }");
|
||||
check_not_applicable("enum A { <|>One(u32, String) }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_from_impl_struct_variant() {
|
||||
check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One { x: u32 } }");
|
||||
check_not_applicable("enum A { <|>One { x: u32 } }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_from_impl_already_exists() {
|
||||
check_assist_not_applicable(
|
||||
add_from_impl_for_enum,
|
||||
r#"enum A { <|>One(u32), }
|
||||
covers!(test_add_from_impl_already_exists);
|
||||
check_not_applicable(
|
||||
r#"
|
||||
enum A { <|>One(u32), }
|
||||
|
||||
impl From<u32> for A {
|
||||
fn from(v: u32) -> Self {
|
||||
A::One(v)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait From<T> {
|
||||
fn from(T) -> Self;
|
||||
}"#,
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use hir::HasSource;
|
||||
use ra_syntax::{
|
||||
ast::{self, edit, make, AstNode, NameOwner},
|
||||
ast::{
|
||||
self,
|
||||
edit::{self, IndentLevel},
|
||||
make, AstNode, NameOwner,
|
||||
},
|
||||
SmolStr,
|
||||
};
|
||||
|
||||
|
@ -40,7 +44,9 @@ enum AddMissingImplMembersMode {
|
|||
// }
|
||||
//
|
||||
// impl Trait<u32> for () {
|
||||
// fn foo(&self) -> u32 { todo!() }
|
||||
// fn foo(&self) -> u32 {
|
||||
// todo!()
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// ```
|
||||
|
@ -165,7 +171,9 @@ fn add_missing_impl_members_inner(
|
|||
|
||||
fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
|
||||
if fn_def.body().is_none() {
|
||||
fn_def.with_body(make::block_from_expr(make::expr_todo()))
|
||||
let body = make::block_expr(None, Some(make::expr_todo()));
|
||||
let body = IndentLevel(1).increase_indent(body);
|
||||
fn_def.with_body(body)
|
||||
} else {
|
||||
fn_def
|
||||
}
|
||||
|
@ -181,7 +189,7 @@ mod tests {
|
|||
fn test_add_missing_impl_members() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
trait Foo {
|
||||
type Output;
|
||||
|
||||
|
@ -197,8 +205,8 @@ struct S;
|
|||
impl Foo for S {
|
||||
fn bar(&self) {}
|
||||
<|>
|
||||
}",
|
||||
"
|
||||
}"#,
|
||||
r#"
|
||||
trait Foo {
|
||||
type Output;
|
||||
|
||||
|
@ -215,10 +223,14 @@ impl Foo for S {
|
|||
fn bar(&self) {}
|
||||
<|>type Output;
|
||||
const CONST: usize = 42;
|
||||
fn foo(&self) { todo!() }
|
||||
fn baz(&self) { todo!() }
|
||||
fn foo(&self) {
|
||||
todo!()
|
||||
}
|
||||
fn baz(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
}",
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -226,7 +238,7 @@ impl Foo for S {
|
|||
fn test_copied_overriden_members() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
trait Foo {
|
||||
fn foo(&self);
|
||||
fn bar(&self) -> bool { true }
|
||||
|
@ -238,8 +250,8 @@ struct S;
|
|||
impl Foo for S {
|
||||
fn bar(&self) {}
|
||||
<|>
|
||||
}",
|
||||
"
|
||||
}"#,
|
||||
r#"
|
||||
trait Foo {
|
||||
fn foo(&self);
|
||||
fn bar(&self) -> bool { true }
|
||||
|
@ -250,9 +262,11 @@ struct S;
|
|||
|
||||
impl Foo for S {
|
||||
fn bar(&self) {}
|
||||
<|>fn foo(&self) { todo!() }
|
||||
<|>fn foo(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
}",
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -260,16 +274,18 @@ impl Foo for S {
|
|||
fn test_empty_impl_def() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
trait Foo { fn foo(&self); }
|
||||
struct S;
|
||||
impl Foo for S { <|> }",
|
||||
"
|
||||
impl Foo for S { <|> }"#,
|
||||
r#"
|
||||
trait Foo { fn foo(&self); }
|
||||
struct S;
|
||||
impl Foo for S {
|
||||
<|>fn foo(&self) { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -277,16 +293,18 @@ impl Foo for S {
|
|||
fn fill_in_type_params_1() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
trait Foo<T> { fn foo(&self, t: T) -> &T; }
|
||||
struct S;
|
||||
impl Foo<u32> for S { <|> }",
|
||||
"
|
||||
impl Foo<u32> for S { <|> }"#,
|
||||
r#"
|
||||
trait Foo<T> { fn foo(&self, t: T) -> &T; }
|
||||
struct S;
|
||||
impl Foo<u32> for S {
|
||||
<|>fn foo(&self, t: u32) -> &u32 { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self, t: u32) -> &u32 {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -294,16 +312,18 @@ impl Foo<u32> for S {
|
|||
fn fill_in_type_params_2() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
trait Foo<T> { fn foo(&self, t: T) -> &T; }
|
||||
struct S;
|
||||
impl<U> Foo<U> for S { <|> }",
|
||||
"
|
||||
impl<U> Foo<U> for S { <|> }"#,
|
||||
r#"
|
||||
trait Foo<T> { fn foo(&self, t: T) -> &T; }
|
||||
struct S;
|
||||
impl<U> Foo<U> for S {
|
||||
<|>fn foo(&self, t: U) -> &U { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self, t: U) -> &U {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -311,16 +331,18 @@ impl<U> Foo<U> for S {
|
|||
fn test_cursor_after_empty_impl_def() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
trait Foo { fn foo(&self); }
|
||||
struct S;
|
||||
impl Foo for S {}<|>",
|
||||
"
|
||||
impl Foo for S {}<|>"#,
|
||||
r#"
|
||||
trait Foo { fn foo(&self); }
|
||||
struct S;
|
||||
impl Foo for S {
|
||||
<|>fn foo(&self) { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -328,22 +350,24 @@ impl Foo for S {
|
|||
fn test_qualify_path_1() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar;
|
||||
trait Foo { fn foo(&self, bar: Bar); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S { <|> }",
|
||||
"
|
||||
impl foo::Foo for S { <|> }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar;
|
||||
trait Foo { fn foo(&self, bar: Bar); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: foo::Bar) { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self, bar: foo::Bar) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -351,22 +375,24 @@ impl foo::Foo for S {
|
|||
fn test_qualify_path_generic() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
trait Foo { fn foo(&self, bar: Bar<u32>); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S { <|> }",
|
||||
"
|
||||
impl foo::Foo for S { <|> }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
trait Foo { fn foo(&self, bar: Bar<u32>); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: foo::Bar<u32>) { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self, bar: foo::Bar<u32>) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -374,22 +400,24 @@ impl foo::Foo for S {
|
|||
fn test_qualify_path_and_substitute_param() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
trait Foo<T> { fn foo(&self, bar: Bar<T>); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo<u32> for S { <|> }",
|
||||
"
|
||||
impl foo::Foo<u32> for S { <|> }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
trait Foo<T> { fn foo(&self, bar: Bar<T>); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo<u32> for S {
|
||||
<|>fn foo(&self, bar: foo::Bar<u32>) { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self, bar: foo::Bar<u32>) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -398,15 +426,15 @@ impl foo::Foo<u32> for S {
|
|||
// when substituting params, the substituted param should not be qualified!
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
mod foo {
|
||||
trait Foo<T> { fn foo(&self, bar: T); }
|
||||
pub struct Param;
|
||||
}
|
||||
struct Param;
|
||||
struct S;
|
||||
impl foo::Foo<Param> for S { <|> }",
|
||||
"
|
||||
impl foo::Foo<Param> for S { <|> }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
trait Foo<T> { fn foo(&self, bar: T); }
|
||||
pub struct Param;
|
||||
|
@ -414,8 +442,10 @@ mod foo {
|
|||
struct Param;
|
||||
struct S;
|
||||
impl foo::Foo<Param> for S {
|
||||
<|>fn foo(&self, bar: Param) { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self, bar: Param) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -423,15 +453,15 @@ impl foo::Foo<Param> for S {
|
|||
fn test_qualify_path_associated_item() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
impl Bar<T> { type Assoc = u32; }
|
||||
trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S { <|> }",
|
||||
"
|
||||
impl foo::Foo for S { <|> }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
impl Bar<T> { type Assoc = u32; }
|
||||
|
@ -439,8 +469,10 @@ mod foo {
|
|||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: foo::Bar<u32>::Assoc) { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self, bar: foo::Bar<u32>::Assoc) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -448,15 +480,15 @@ impl foo::Foo for S {
|
|||
fn test_qualify_path_nested() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
pub struct Baz;
|
||||
trait Foo { fn foo(&self, bar: Bar<Baz>); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S { <|> }",
|
||||
"
|
||||
impl foo::Foo for S { <|> }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
pub struct Baz;
|
||||
|
@ -464,8 +496,10 @@ mod foo {
|
|||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: foo::Bar<foo::Baz>) { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self, bar: foo::Bar<foo::Baz>) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -473,22 +507,24 @@ impl foo::Foo for S {
|
|||
fn test_qualify_path_fn_trait_notation() {
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
mod foo {
|
||||
pub trait Fn<Args> { type Output; }
|
||||
trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S { <|> }",
|
||||
"
|
||||
impl foo::Foo for S { <|> }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
pub trait Fn<Args> { type Output; }
|
||||
trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: dyn Fn(u32) -> i32) { todo!() }
|
||||
}",
|
||||
<|>fn foo(&self, bar: dyn Fn(u32) -> i32) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -496,10 +532,10 @@ impl foo::Foo for S {
|
|||
fn test_empty_trait() {
|
||||
check_assist_not_applicable(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
trait Foo;
|
||||
struct S;
|
||||
impl Foo for S { <|> }",
|
||||
impl Foo for S { <|> }"#,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -507,13 +543,13 @@ impl Foo for S { <|> }",
|
|||
fn test_ignore_unnamed_trait_members_and_default_methods() {
|
||||
check_assist_not_applicable(
|
||||
add_missing_impl_members,
|
||||
"
|
||||
r#"
|
||||
trait Foo {
|
||||
fn (arg: u32);
|
||||
fn valid(some: u32) -> bool { false }
|
||||
}
|
||||
struct S;
|
||||
impl Foo for S { <|> }",
|
||||
impl Foo for S { <|> }"#,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -544,7 +580,9 @@ trait Foo {
|
|||
struct S;
|
||||
impl Foo for S {
|
||||
<|>type Output;
|
||||
fn foo(&self) { todo!() }
|
||||
fn foo(&self) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
@ -553,7 +591,7 @@ impl Foo for S {
|
|||
fn test_default_methods() {
|
||||
check_assist(
|
||||
add_missing_default_members,
|
||||
"
|
||||
r#"
|
||||
trait Foo {
|
||||
type Output;
|
||||
|
||||
|
@ -563,8 +601,8 @@ trait Foo {
|
|||
fn foo(some: u32) -> bool;
|
||||
}
|
||||
struct S;
|
||||
impl Foo for S { <|> }",
|
||||
"
|
||||
impl Foo for S { <|> }"#,
|
||||
r#"
|
||||
trait Foo {
|
||||
type Output;
|
||||
|
||||
|
@ -576,7 +614,7 @@ trait Foo {
|
|||
struct S;
|
||||
impl Foo for S {
|
||||
<|>fn valid(some: u32) -> bool { false }
|
||||
}",
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{iter::once, ops::RangeInclusive};
|
|||
|
||||
use ra_syntax::{
|
||||
algo::replace_children,
|
||||
ast::{self, edit::IndentLevel, make, Block, Pat::TupleStructPat},
|
||||
ast::{self, edit::IndentLevel, make},
|
||||
AstNode,
|
||||
SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
|
||||
SyntaxNode,
|
||||
|
@ -47,7 +47,7 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
|
|||
// Check if there is an IfLet that we can handle.
|
||||
let if_let_pat = match cond.pat() {
|
||||
None => None, // No IfLet, supported.
|
||||
Some(TupleStructPat(pat)) if pat.args().count() == 1 => {
|
||||
Some(ast::Pat::TupleStructPat(pat)) if pat.args().count() == 1 => {
|
||||
let path = pat.path()?;
|
||||
match path.qualifier() {
|
||||
None => {
|
||||
|
@ -61,9 +61,9 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
|
|||
};
|
||||
|
||||
let cond_expr = cond.expr()?;
|
||||
let then_block = if_expr.then_branch()?.block()?;
|
||||
let then_block = if_expr.then_branch()?;
|
||||
|
||||
let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::Block::cast)?;
|
||||
let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
|
||||
|
||||
if parent_block.expr()? != if_expr.clone().into() {
|
||||
return None;
|
||||
|
@ -80,7 +80,7 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let parent_container = parent_block.syntax().parent()?.parent()?;
|
||||
let parent_container = parent_block.syntax().parent()?;
|
||||
|
||||
let early_expression: ast::Expr = match parent_container.kind() {
|
||||
WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
|
||||
|
@ -144,13 +144,13 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
|
|||
}
|
||||
};
|
||||
edit.target(if_expr.syntax().text_range());
|
||||
edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap());
|
||||
edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
|
||||
edit.set_cursor(cursor_position);
|
||||
|
||||
fn replace(
|
||||
new_expr: &SyntaxNode,
|
||||
then_block: &Block,
|
||||
parent_block: &Block,
|
||||
then_block: &ast::BlockExpr,
|
||||
parent_block: &ast::BlockExpr,
|
||||
if_expr: &ast::IfExpr,
|
||||
) -> SyntaxNode {
|
||||
let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
|
||||
|
|
|
@ -89,6 +89,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
|
|||
| (ast::Expr::ParenExpr(_), _)
|
||||
| (ast::Expr::PathExpr(_), _)
|
||||
| (ast::Expr::BlockExpr(_), _)
|
||||
| (ast::Expr::EffectExpr(_), _)
|
||||
| (_, ast::Expr::CallExpr(_))
|
||||
| (_, ast::Expr::TupleExpr(_))
|
||||
| (_, ast::Expr::ArrayExpr(_))
|
||||
|
|
|
@ -111,7 +111,7 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
|
|||
/// expression like a lambda or match arm.
|
||||
fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
|
||||
expr.syntax().ancestors().find_map(|node| {
|
||||
if let Some(expr) = node.parent().and_then(ast::Block::cast).and_then(|it| it.expr()) {
|
||||
if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) {
|
||||
if expr.syntax() == &node {
|
||||
tested_by!(test_introduce_var_last_expr);
|
||||
return Some((node, false));
|
||||
|
|
|
@ -113,9 +113,9 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> {
|
|||
"Move condition to match guard",
|
||||
|edit| {
|
||||
edit.target(if_expr.syntax().text_range());
|
||||
let then_only_expr = then_block.block().and_then(|it| it.statements().next()).is_none();
|
||||
let then_only_expr = then_block.statements().next().is_none();
|
||||
|
||||
match &then_block.block().and_then(|it| it.expr()) {
|
||||
match &then_block.expr() {
|
||||
Some(then_expr) if then_only_expr => {
|
||||
edit.replace(if_expr.syntax().text_range(), then_expr.syntax().text())
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use ra_fmt::unwrap_trivial_block;
|
||||
use ra_syntax::{
|
||||
ast::{self, make},
|
||||
ast::{self, edit::IndentLevel, make},
|
||||
AstNode,
|
||||
};
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
use ast::edit::IndentLevel;
|
||||
use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
|
||||
|
||||
// Assist: replace_if_let_with_match
|
||||
//
|
||||
|
@ -44,15 +43,21 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
|
|||
ast::ElseBranch::IfExpr(_) => return None,
|
||||
};
|
||||
|
||||
ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| {
|
||||
let sema = ctx.sema;
|
||||
ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", move |edit| {
|
||||
let match_expr = {
|
||||
let then_arm = {
|
||||
let then_expr = unwrap_trivial_block(then_block);
|
||||
make::match_arm(vec![pat], then_expr)
|
||||
make::match_arm(vec![pat.clone()], then_expr)
|
||||
};
|
||||
let else_arm = {
|
||||
let pattern = sema
|
||||
.type_of_pat(&pat)
|
||||
.and_then(|ty| TryEnum::from_ty(sema, &ty))
|
||||
.map(|it| it.sad_pattern())
|
||||
.unwrap_or_else(|| make::placeholder_pat().into());
|
||||
let else_expr = unwrap_trivial_block(else_block);
|
||||
make::match_arm(vec![make::placeholder_pat().into()], else_expr)
|
||||
make::match_arm(vec![pattern], else_expr)
|
||||
};
|
||||
make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
|
||||
};
|
||||
|
@ -68,6 +73,7 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::helpers::{check_assist, check_assist_target};
|
||||
|
||||
#[test]
|
||||
|
@ -145,4 +151,64 @@ impl VariantData {
|
|||
}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn special_case_option() {
|
||||
check_assist(
|
||||
replace_if_let_with_match,
|
||||
r#"
|
||||
enum Option<T> { Some(T), None }
|
||||
use Option::*;
|
||||
|
||||
fn foo(x: Option<i32>) {
|
||||
<|>if let Some(x) = x {
|
||||
println!("{}", x)
|
||||
} else {
|
||||
println!("none")
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
enum Option<T> { Some(T), None }
|
||||
use Option::*;
|
||||
|
||||
fn foo(x: Option<i32>) {
|
||||
<|>match x {
|
||||
Some(x) => println!("{}", x),
|
||||
None => println!("none"),
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn special_case_result() {
|
||||
check_assist(
|
||||
replace_if_let_with_match,
|
||||
r#"
|
||||
enum Result<T, E> { Ok(T), Err(E) }
|
||||
use Result::*;
|
||||
|
||||
fn foo(x: Result<i32, ()>) {
|
||||
<|>if let Ok(x) = x {
|
||||
println!("{}", x)
|
||||
} else {
|
||||
println!("none")
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
enum Result<T, E> { Ok(T), Err(E) }
|
||||
use Result::*;
|
||||
|
||||
fn foo(x: Result<i32, ()>) {
|
||||
<|>match x {
|
||||
Ok(x) => println!("{}", x),
|
||||
Err(_) => println!("none"),
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::iter::once;
|
||||
|
||||
use hir::Adt;
|
||||
use ra_syntax::{
|
||||
ast::{
|
||||
self,
|
||||
|
@ -12,6 +11,7 @@ use ra_syntax::{
|
|||
|
||||
use crate::{
|
||||
assist_ctx::{Assist, AssistCtx},
|
||||
utils::TryEnum,
|
||||
AssistId,
|
||||
};
|
||||
|
||||
|
@ -45,20 +45,10 @@ pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
|
|||
let init = let_stmt.initializer()?;
|
||||
let original_pat = let_stmt.pat()?;
|
||||
let ty = ctx.sema.type_of_expr(&init)?;
|
||||
let enum_ = match ty.as_adt() {
|
||||
Some(Adt::Enum(it)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
let happy_case =
|
||||
[("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| {
|
||||
if &enum_.name(ctx.db).to_string() == known_type {
|
||||
return Some(happy_case);
|
||||
}
|
||||
None
|
||||
});
|
||||
let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case());
|
||||
|
||||
ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| {
|
||||
let with_placeholder: ast::Pat = match happy_case {
|
||||
let with_placeholder: ast::Pat = match happy_variant {
|
||||
None => make::placeholder_pat().into(),
|
||||
Some(var_name) => make::tuple_struct_pat(
|
||||
make::path_unqualified(make::path_segment(make::name_ref(var_name))),
|
||||
|
|
|
@ -27,7 +27,7 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist>
|
|||
return None;
|
||||
}
|
||||
|
||||
let hir_path = hir::Path::from_ast(path.clone())?;
|
||||
let hir_path = ctx.sema.lower_path(&path)?;
|
||||
let segments = collect_hir_path_segments(&hir_path)?;
|
||||
if segments.len() < 2 {
|
||||
return None;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use std::iter;
|
||||
|
||||
use ra_syntax::{
|
||||
ast::{self, make},
|
||||
ast::{self, edit::IndentLevel, make},
|
||||
AstNode,
|
||||
};
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
use ast::edit::IndentLevel;
|
||||
use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
|
||||
|
||||
// Assist: replace_unwrap_with_match
|
||||
//
|
||||
|
@ -38,42 +37,27 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
|
|||
}
|
||||
let caller = method_call.expr()?;
|
||||
let ty = ctx.sema.type_of_expr(&caller)?;
|
||||
let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case();
|
||||
|
||||
let type_name = ty.as_adt()?.name(ctx.sema.db).to_string();
|
||||
ctx.add_assist(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", |edit| {
|
||||
let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
|
||||
let it = make::bind_pat(make::name("a")).into();
|
||||
let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
|
||||
|
||||
for (unwrap_type, variant_name) in [("Result", "Ok"), ("Option", "Some")].iter() {
|
||||
if &type_name == unwrap_type {
|
||||
return ctx.add_assist(
|
||||
AssistId("replace_unwrap_with_match"),
|
||||
"Replace unwrap with match",
|
||||
|edit| {
|
||||
let ok_path =
|
||||
make::path_unqualified(make::path_segment(make::name_ref(variant_name)));
|
||||
let it = make::bind_pat(make::name("a")).into();
|
||||
let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
|
||||
let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
|
||||
let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
|
||||
|
||||
let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
|
||||
let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
|
||||
let unreachable_call = make::unreachable_macro_call().into();
|
||||
let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
|
||||
|
||||
let unreachable_call = make::unreachable_macro_call().into();
|
||||
let err_arm = make::match_arm(
|
||||
iter::once(make::placeholder_pat().into()),
|
||||
unreachable_call,
|
||||
);
|
||||
let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
|
||||
let match_expr = make::expr_match(caller.clone(), match_arm_list);
|
||||
let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr);
|
||||
|
||||
let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
|
||||
let match_expr = make::expr_match(caller.clone(), match_arm_list);
|
||||
let match_expr =
|
||||
IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr);
|
||||
|
||||
edit.target(method_call.syntax().text_range());
|
||||
edit.set_cursor(caller.syntax().text_range().start());
|
||||
edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
None
|
||||
edit.target(method_call.syntax().text_range());
|
||||
edit.set_cursor(caller.syntax().text_range().start());
|
||||
edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
348
crates/ra_assists/src/handlers/unwrap_block.rs
Normal file
348
crates/ra_assists/src/handlers/unwrap_block.rs
Normal file
|
@ -0,0 +1,348 @@
|
|||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr};
|
||||
use ra_fmt::unwrap_trivial_block;
|
||||
use ra_syntax::{ast, AstNode, TextRange, T};
|
||||
|
||||
// Assist: unwrap_block
|
||||
//
|
||||
// This assist removes if...else, for, while and loop control statements to just keep the body.
|
||||
//
|
||||
// ```
|
||||
// fn foo() {
|
||||
// if true {<|>
|
||||
// println!("foo");
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn foo() {
|
||||
// println!("foo");
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
|
||||
let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
|
||||
|
||||
let res = if let Some(if_expr) = l_curly_token.ancestors().find_map(IfExpr::cast) {
|
||||
// if expression
|
||||
let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr));
|
||||
let expr_to_unwrap = expr_to_unwrap?;
|
||||
// Find if we are in a else if block
|
||||
let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast);
|
||||
|
||||
if let Some(ancestor) = ancestor {
|
||||
Some((ast::Expr::IfExpr(ancestor), expr_to_unwrap))
|
||||
} else {
|
||||
Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap))
|
||||
}
|
||||
} else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) {
|
||||
// for expression
|
||||
let block_expr = for_expr.loop_body()?;
|
||||
extract_expr(ctx.frange.range, block_expr)
|
||||
.map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap))
|
||||
} else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) {
|
||||
// while expression
|
||||
let block_expr = while_expr.loop_body()?;
|
||||
extract_expr(ctx.frange.range, block_expr)
|
||||
.map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap))
|
||||
} else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) {
|
||||
// loop expression
|
||||
let block_expr = loop_expr.loop_body()?;
|
||||
extract_expr(ctx.frange.range, block_expr)
|
||||
.map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (expr, expr_to_unwrap) = res?;
|
||||
ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| {
|
||||
edit.set_cursor(expr.syntax().text_range().start());
|
||||
edit.target(expr_to_unwrap.syntax().text_range());
|
||||
|
||||
let pat_start: &[_] = &[' ', '{', '\n'];
|
||||
let expr_to_unwrap = expr_to_unwrap.to_string();
|
||||
let expr_string = expr_to_unwrap.trim_start_matches(pat_start);
|
||||
let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
|
||||
expr_string_lines.pop(); // Delete last line
|
||||
|
||||
let expr_string = expr_string_lines
|
||||
.into_iter()
|
||||
.map(|line| line.replacen(" ", "", 1)) // Delete indentation
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
edit.replace(expr.syntax().text_range(), expr_string);
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_expr(cursor_range: TextRange, block: BlockExpr) -> Option<Expr> {
|
||||
let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
|
||||
|
||||
if cursor_in_range {
|
||||
Some(unwrap_trivial_block(block))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::helpers::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn simple_if() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
if true {<|>
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
<|>foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_else() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {<|>
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
<|>println!("bar");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_else_if() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
//bar();
|
||||
if true {
|
||||
println!("true");
|
||||
|
||||
//comment
|
||||
//bar();
|
||||
} else if false {<|>
|
||||
println!("bar");
|
||||
} else {
|
||||
println!("foo");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
//bar();
|
||||
<|>println!("bar");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_bad_cursor_position() {
|
||||
check_assist_not_applicable(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();<|>
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_for() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
for i in 0..5 {<|>
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
<|>if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_in_for() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
for i in 0..5 {
|
||||
if true {<|>
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
for i in 0..5 {
|
||||
<|>foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_loop() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
loop {<|>
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
<|>if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_while() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
while true {<|>
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
<|>if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_in_while_bad_cursor_position() {
|
||||
check_assist_not_applicable(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
while true {
|
||||
if true {
|
||||
foo();<|>
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -143,6 +143,7 @@ mod handlers {
|
|||
mod split_import;
|
||||
mod add_from_impl_for_enum;
|
||||
mod reorder_fields;
|
||||
mod unwrap_block;
|
||||
|
||||
pub(crate) fn all() -> &'static [AssistHandler] {
|
||||
&[
|
||||
|
@ -181,6 +182,7 @@ mod handlers {
|
|||
replace_unwrap_with_match::replace_unwrap_with_match,
|
||||
split_import::split_import,
|
||||
add_from_impl_for_enum::add_from_impl_for_enum,
|
||||
unwrap_block::unwrap_block,
|
||||
// These are manually sorted for better priorities
|
||||
add_missing_impl_members::add_missing_impl_members,
|
||||
add_missing_impl_members::add_missing_default_members,
|
||||
|
|
|
@ -8,4 +8,5 @@ test_utils::marks![
|
|||
test_not_inline_mut_variable
|
||||
test_not_applicable_if_variable_unused
|
||||
change_visibility_field_false_positive
|
||||
test_add_from_impl_already_exists
|
||||
];
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
//! Assorted functions shared by several assists.
|
||||
pub(crate) mod insert_use;
|
||||
|
||||
use hir::Semantics;
|
||||
use std::iter;
|
||||
|
||||
use hir::{Adt, Crate, Semantics, Trait, Type};
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, make, NameOwner},
|
||||
|
@ -99,3 +101,109 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum TryEnum {
|
||||
Result,
|
||||
Option,
|
||||
}
|
||||
|
||||
impl TryEnum {
|
||||
const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result];
|
||||
|
||||
pub(crate) fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> {
|
||||
let enum_ = match ty.as_adt() {
|
||||
Some(Adt::Enum(it)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
TryEnum::ALL.iter().find_map(|&var| {
|
||||
if &enum_.name(sema.db).to_string() == var.type_name() {
|
||||
return Some(var);
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn happy_case(self) -> &'static str {
|
||||
match self {
|
||||
TryEnum::Result => "Ok",
|
||||
TryEnum::Option => "Some",
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sad_pattern(self) -> ast::Pat {
|
||||
match self {
|
||||
TryEnum::Result => make::tuple_struct_pat(
|
||||
make::path_unqualified(make::path_segment(make::name_ref("Err"))),
|
||||
iter::once(make::placeholder_pat().into()),
|
||||
)
|
||||
.into(),
|
||||
TryEnum::Option => make::bind_pat(make::name("None")).into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn type_name(self) -> &'static str {
|
||||
match self {
|
||||
TryEnum::Result => "Result",
|
||||
TryEnum::Option => "Option",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helps with finding well-know things inside the standard library. This is
|
||||
/// somewhat similar to the known paths infra inside hir, but it different; We
|
||||
/// want to make sure that IDE specific paths don't become interesting inside
|
||||
/// the compiler itself as well.
|
||||
pub(crate) struct FamousDefs<'a, 'b>(pub(crate) &'a Semantics<'b, RootDatabase>, pub(crate) Crate);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl FamousDefs<'_, '_> {
|
||||
#[cfg(test)]
|
||||
pub(crate) const FIXTURE: &'static str = r#"
|
||||
//- /libcore.rs crate:core
|
||||
pub mod convert{
|
||||
pub trait From<T> {
|
||||
fn from(T) -> Self;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod prelude { pub use crate::convert::From }
|
||||
#[prelude_import]
|
||||
pub use prelude::*;
|
||||
"#;
|
||||
|
||||
pub(crate) fn core_convert_From(&self) -> Option<Trait> {
|
||||
self.find_trait("core:convert:From")
|
||||
}
|
||||
|
||||
fn find_trait(&self, path: &str) -> Option<Trait> {
|
||||
let db = self.0.db;
|
||||
let mut path = path.split(':');
|
||||
let trait_ = path.next_back()?;
|
||||
let std_crate = path.next()?;
|
||||
let std_crate = self
|
||||
.1
|
||||
.dependencies(db)
|
||||
.into_iter()
|
||||
.find(|dep| &dep.name.to_string() == std_crate)?
|
||||
.krate;
|
||||
|
||||
let mut module = std_crate.root_module(db)?;
|
||||
for segment in path {
|
||||
module = module.children(db).find_map(|child| {
|
||||
let name = child.name(db)?;
|
||||
if &name.to_string() == segment {
|
||||
Some(child)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
}
|
||||
let def =
|
||||
module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1;
|
||||
match def {
|
||||
hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ authors = ["rust-analyzer developers"]
|
|||
|
||||
[dependencies]
|
||||
crossbeam-channel = "0.4.0"
|
||||
lsp-types = { version = "0.73.0", features = ["proposed"] }
|
||||
lsp-types = { version = "0.74.0", features = ["proposed"] }
|
||||
log = "0.4.8"
|
||||
cargo_metadata = "0.9.1"
|
||||
serde_json = "1.0.48"
|
||||
|
|
|
@ -42,7 +42,6 @@ pub fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
|
|||
}
|
||||
|
||||
pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
|
||||
let block = block.block()?;
|
||||
let has_anything_else = |thing: &SyntaxNode| -> bool {
|
||||
let mut non_trivial_children =
|
||||
block.syntax().children_with_tokens().filter(|it| match it.kind() {
|
||||
|
@ -57,18 +56,17 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
|
|||
return None;
|
||||
}
|
||||
return Some(expr);
|
||||
} else {
|
||||
// Unwrap `{ continue; }`
|
||||
let (stmt,) = block.statements().next_tuple()?;
|
||||
if let ast::Stmt::ExprStmt(expr_stmt) = stmt {
|
||||
if has_anything_else(expr_stmt.syntax()) {
|
||||
return None;
|
||||
}
|
||||
let expr = expr_stmt.expr()?;
|
||||
match expr.syntax().kind() {
|
||||
CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR => return Some(expr),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// Unwrap `{ continue; }`
|
||||
let (stmt,) = block.statements().next_tuple()?;
|
||||
if let ast::Stmt::ExprStmt(expr_stmt) = stmt {
|
||||
if has_anything_else(expr_stmt.syntax()) {
|
||||
return None;
|
||||
}
|
||||
let expr = expr_stmt.expr()?;
|
||||
match expr.syntax().kind() {
|
||||
CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR => return Some(expr),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
@ -953,6 +953,16 @@ impl TypeParam {
|
|||
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||
self.id.parent.module(db.upcast()).into()
|
||||
}
|
||||
|
||||
pub fn ty(self, db: &dyn HirDatabase) -> Type {
|
||||
let resolver = self.id.parent.resolver(db.upcast());
|
||||
let environment = TraitEnvironment::lower(db, &resolver);
|
||||
let ty = Ty::Placeholder(self.id);
|
||||
Type {
|
||||
krate: self.id.parent.module(db.upcast()).krate,
|
||||
ty: InEnvironment { value: ty, environment },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: rename from `ImplDef` to `Impl`
|
||||
|
@ -1157,18 +1167,21 @@ impl Type {
|
|||
|
||||
pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
|
||||
if let Ty::Apply(a_ty) = &self.ty.value {
|
||||
if let TypeCtor::Adt(AdtId::StructId(s)) = a_ty.ctor {
|
||||
let var_def = s.into();
|
||||
return db
|
||||
.field_types(var_def)
|
||||
.iter()
|
||||
.map(|(local_id, ty)| {
|
||||
let def = Field { parent: var_def.into(), id: local_id };
|
||||
let ty = ty.clone().subst(&a_ty.parameters);
|
||||
(def, self.derived(ty))
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
let variant_id = match a_ty.ctor {
|
||||
TypeCtor::Adt(AdtId::StructId(s)) => s.into(),
|
||||
TypeCtor::Adt(AdtId::UnionId(u)) => u.into(),
|
||||
_ => return Vec::new(),
|
||||
};
|
||||
|
||||
return db
|
||||
.field_types(variant_id)
|
||||
.iter()
|
||||
.map(|(local_id, ty)| {
|
||||
let def = Field { parent: variant_id.into(), id: local_id };
|
||||
let ty = ty.clone().subst(&a_ty.parameters);
|
||||
(def, self.derived(ty))
|
||||
})
|
||||
.collect();
|
||||
};
|
||||
Vec::new()
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ pub use hir_def::{
|
|||
type_ref::Mutability,
|
||||
};
|
||||
pub use hir_expand::{
|
||||
name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin,
|
||||
hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId,
|
||||
MacroFile, Origin,
|
||||
};
|
||||
pub use hir_ty::{display::HirDisplay, CallableDef};
|
||||
|
|
|
@ -8,7 +8,8 @@ use hir_def::{
|
|||
resolver::{self, HasResolver, Resolver},
|
||||
AsMacroCall, TraitId,
|
||||
};
|
||||
use hir_expand::ExpansionInfo;
|
||||
use hir_expand::{hygiene::Hygiene, ExpansionInfo};
|
||||
use hir_ty::associated_type_shorthand_candidates;
|
||||
use itertools::Itertools;
|
||||
use ra_db::{FileId, FileRange};
|
||||
use ra_prof::profile;
|
||||
|
@ -24,8 +25,9 @@ use crate::{
|
|||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
||||
AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef,
|
||||
Name, Origin, Path, ScopeDef, Trait, Type, TypeParam,
|
||||
Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam,
|
||||
};
|
||||
use resolver::TypeNs;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PathResolution {
|
||||
|
@ -40,6 +42,44 @@ pub enum PathResolution {
|
|||
AssocItem(AssocItem),
|
||||
}
|
||||
|
||||
impl PathResolution {
|
||||
fn in_type_ns(&self) -> Option<TypeNs> {
|
||||
match self {
|
||||
PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())),
|
||||
PathResolution::Def(ModuleDef::BuiltinType(builtin)) => {
|
||||
Some(TypeNs::BuiltinType(*builtin))
|
||||
}
|
||||
PathResolution::Def(ModuleDef::Const(_))
|
||||
| PathResolution::Def(ModuleDef::EnumVariant(_))
|
||||
| PathResolution::Def(ModuleDef::Function(_))
|
||||
| PathResolution::Def(ModuleDef::Module(_))
|
||||
| PathResolution::Def(ModuleDef::Static(_))
|
||||
| PathResolution::Def(ModuleDef::Trait(_)) => None,
|
||||
PathResolution::Def(ModuleDef::TypeAlias(alias)) => {
|
||||
Some(TypeNs::TypeAliasId((*alias).into()))
|
||||
}
|
||||
PathResolution::Local(_) | PathResolution::Macro(_) => None,
|
||||
PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
|
||||
PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
|
||||
PathResolution::AssocItem(AssocItem::Const(_))
|
||||
| PathResolution::AssocItem(AssocItem::Function(_)) => None,
|
||||
PathResolution::AssocItem(AssocItem::TypeAlias(alias)) => {
|
||||
Some(TypeNs::TypeAliasId((*alias).into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over associated types that may be specified after this path (using
|
||||
/// `Ty::Assoc` syntax).
|
||||
pub fn assoc_type_shorthand_candidates<R>(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
mut cb: impl FnMut(TypeAlias) -> Option<R>,
|
||||
) -> Option<R> {
|
||||
associated_type_shorthand_candidates(db, self.in_type_ns()?, |_, _, id| cb(id.into()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Primary API to get semantic information, like types, from syntax trees.
|
||||
pub struct Semantics<'db, DB> {
|
||||
pub db: &'db DB,
|
||||
|
@ -206,6 +246,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
|||
self.analyze(path.syntax()).resolve_path(self.db, path)
|
||||
}
|
||||
|
||||
pub fn lower_path(&self, path: &ast::Path) -> Option<Path> {
|
||||
let src = self.find_file(path.syntax().clone());
|
||||
Path::from_src(path.clone(), &Hygiene::new(self.db.upcast(), src.file_id.into()))
|
||||
}
|
||||
|
||||
pub fn resolve_bind_pat_to_const(&self, pat: &ast::BindPat) -> Option<ModuleDef> {
|
||||
self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ impl SourceToDefCtx<'_, '_> {
|
|||
let krate = self.file_to_def(file_id)?.krate;
|
||||
let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
|
||||
let ast_id = Some(AstId::new(src.file_id, file_ast_id));
|
||||
Some(MacroDefId { krate: Some(krate), ast_id, kind })
|
||||
Some(MacroDefId { krate: Some(krate), ast_id, kind, local_inner: false })
|
||||
}
|
||||
|
||||
pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
|
||||
|
|
|
@ -224,7 +224,8 @@ impl SourceAnalyzer {
|
|||
}
|
||||
}
|
||||
// This must be a normal source file rather than macro file.
|
||||
let hir_path = crate::Path::from_ast(path.clone())?;
|
||||
let hir_path =
|
||||
crate::Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?;
|
||||
resolve_hir_path(db, &self.resolver, &hir_path)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,15 @@ use ra_prof::profile;
|
|||
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
|
||||
|
||||
use crate::{
|
||||
body::CfgExpander, db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace,
|
||||
type_ref::TypeRef, visibility::RawVisibility, EnumId, HasModule, LocalEnumVariantId,
|
||||
LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
|
||||
body::{CfgExpander, LowerCtx},
|
||||
db::DefDatabase,
|
||||
src::HasChildSource,
|
||||
src::HasSource,
|
||||
trace::Trace,
|
||||
type_ref::TypeRef,
|
||||
visibility::RawVisibility,
|
||||
EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId,
|
||||
VariantId,
|
||||
};
|
||||
|
||||
/// Note that we use `StructData` for unions as well!
|
||||
|
@ -198,6 +204,8 @@ fn lower_struct(
|
|||
trace: &mut Trace<FieldData, Either<ast::TupleFieldDef, ast::RecordFieldDef>>,
|
||||
ast: &InFile<ast::StructKind>,
|
||||
) -> StructKind {
|
||||
let ctx = LowerCtx::new(db, ast.file_id);
|
||||
|
||||
match &ast.value {
|
||||
ast::StructKind::Tuple(fl) => {
|
||||
for (i, fd) in fl.fields().enumerate() {
|
||||
|
@ -210,7 +218,7 @@ fn lower_struct(
|
|||
|| Either::Left(fd.clone()),
|
||||
|| FieldData {
|
||||
name: Name::new_tuple_field(i),
|
||||
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
|
||||
type_ref: TypeRef::from_ast_opt(&ctx, fd.type_ref()),
|
||||
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
|
||||
},
|
||||
);
|
||||
|
@ -228,7 +236,7 @@ fn lower_struct(
|
|||
|| Either::Right(fd.clone()),
|
||||
|| FieldData {
|
||||
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
|
||||
type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
|
||||
type_ref: TypeRef::from_ast_opt(&ctx, fd.ascribed_type()),
|
||||
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
|
||||
},
|
||||
);
|
||||
|
|
|
@ -140,6 +140,7 @@ impl Attr {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AttrQuery<'a> {
|
||||
attrs: &'a Attrs,
|
||||
key: &'static str,
|
||||
|
|
|
@ -15,6 +15,8 @@ use ra_prof::profile;
|
|||
use ra_syntax::{ast, AstNode, AstPtr};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
pub(crate) use lower::LowerCtx;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName, Name},
|
||||
MacroDefId, MacroDefKind,
|
||||
HirFileId, MacroDefId, MacroDefKind,
|
||||
};
|
||||
use ra_arena::Arena;
|
||||
use ra_syntax::{
|
||||
|
@ -26,7 +27,7 @@ use crate::{
|
|||
LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
|
||||
},
|
||||
item_scope::BuiltinShadowMode,
|
||||
path::GenericArgs,
|
||||
path::{GenericArgs, Path},
|
||||
type_ref::{Mutability, TypeRef},
|
||||
AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
|
||||
StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
|
||||
|
@ -35,6 +36,23 @@ use crate::{
|
|||
use super::{ExprSource, PatSource};
|
||||
use ast::AstChildren;
|
||||
|
||||
pub(crate) struct LowerCtx {
|
||||
hygiene: Hygiene,
|
||||
}
|
||||
|
||||
impl LowerCtx {
|
||||
pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self {
|
||||
LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) }
|
||||
}
|
||||
pub fn with_hygiene(hygiene: &Hygiene) -> Self {
|
||||
LowerCtx { hygiene: hygiene.clone() }
|
||||
}
|
||||
|
||||
pub fn lower_path(&self, ast: ast::Path) -> Option<Path> {
|
||||
Path::from_src(ast, &self.hygiene)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower(
|
||||
db: &dyn DefDatabase,
|
||||
def: DefWithBodyId,
|
||||
|
@ -42,10 +60,13 @@ pub(super) fn lower(
|
|||
params: Option<ast::ParamList>,
|
||||
body: Option<ast::Expr>,
|
||||
) -> (Body, BodySourceMap) {
|
||||
let ctx = LowerCtx::new(db, expander.current_file_id.clone());
|
||||
|
||||
ExprCollector {
|
||||
db,
|
||||
def,
|
||||
expander,
|
||||
ctx,
|
||||
source_map: BodySourceMap::default(),
|
||||
body: Body {
|
||||
exprs: Arena::default(),
|
||||
|
@ -62,7 +83,7 @@ struct ExprCollector<'a> {
|
|||
db: &'a dyn DefDatabase,
|
||||
def: DefWithBodyId,
|
||||
expander: Expander,
|
||||
|
||||
ctx: LowerCtx,
|
||||
body: Body,
|
||||
source_map: BodySourceMap,
|
||||
}
|
||||
|
@ -182,10 +203,16 @@ impl ExprCollector<'_> {
|
|||
|
||||
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::TryBlockExpr(e) => {
|
||||
let body = self.collect_block_opt(e.body());
|
||||
self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::EffectExpr(e) => match e.effect() {
|
||||
ast::Effect::Try(_) => {
|
||||
let body = self.collect_block_opt(e.block_expr());
|
||||
self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
|
||||
}
|
||||
// FIXME: we need to record these effects somewhere...
|
||||
ast::Effect::Async(_) | ast::Effect::Label(_) | ast::Effect::Unsafe(_) => {
|
||||
self.collect_block_opt(e.block_expr())
|
||||
}
|
||||
},
|
||||
ast::Expr::BlockExpr(e) => self.collect_block(e),
|
||||
ast::Expr::LoopExpr(e) => {
|
||||
let body = self.collect_block_opt(e.loop_body());
|
||||
|
@ -241,7 +268,8 @@ impl ExprCollector<'_> {
|
|||
Vec::new()
|
||||
};
|
||||
let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
|
||||
let generic_args = e.type_arg_list().and_then(GenericArgs::from_ast);
|
||||
let generic_args =
|
||||
e.type_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx, it));
|
||||
self.alloc_expr(
|
||||
Expr::MethodCall { receiver, method_name, args, generic_args },
|
||||
syntax_ptr,
|
||||
|
@ -347,7 +375,7 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
ast::Expr::CastExpr(e) => {
|
||||
let expr = self.collect_expr_opt(e.expr());
|
||||
let type_ref = TypeRef::from_ast_opt(e.type_ref());
|
||||
let type_ref = TypeRef::from_ast_opt(&self.ctx, e.type_ref());
|
||||
self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::RefExpr(e) => {
|
||||
|
@ -369,12 +397,16 @@ impl ExprCollector<'_> {
|
|||
if let Some(pl) = e.param_list() {
|
||||
for param in pl.params() {
|
||||
let pat = self.collect_pat_opt(param.pat());
|
||||
let type_ref = param.ascribed_type().map(TypeRef::from_ast);
|
||||
let type_ref =
|
||||
param.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx, it));
|
||||
args.push(pat);
|
||||
arg_types.push(type_ref);
|
||||
}
|
||||
}
|
||||
let ret_type = e.ret_type().and_then(|r| r.type_ref()).map(TypeRef::from_ast);
|
||||
let ret_type = e
|
||||
.ret_type()
|
||||
.and_then(|r| r.type_ref())
|
||||
.map(|it| TypeRef::from_ast(&self.ctx, it));
|
||||
let body = self.collect_expr_opt(e.body());
|
||||
self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr)
|
||||
}
|
||||
|
@ -434,6 +466,7 @@ impl ExprCollector<'_> {
|
|||
krate: Some(self.expander.module.krate),
|
||||
ast_id: Some(self.expander.ast_id(&e)),
|
||||
kind: MacroDefKind::Declarative,
|
||||
local_inner: false,
|
||||
};
|
||||
self.body.item_scope.define_legacy_macro(name, mac);
|
||||
|
||||
|
@ -468,19 +501,15 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn collect_block(&mut self, expr: ast::BlockExpr) -> ExprId {
|
||||
let syntax_node_ptr = AstPtr::new(&expr.clone().into());
|
||||
let block = match expr.block() {
|
||||
Some(block) => block,
|
||||
None => return self.alloc_expr(Expr::Missing, syntax_node_ptr),
|
||||
};
|
||||
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
|
||||
let syntax_node_ptr = AstPtr::new(&block.clone().into());
|
||||
self.collect_block_items(&block);
|
||||
let statements = block
|
||||
.statements()
|
||||
.map(|s| match s {
|
||||
ast::Stmt::LetStmt(stmt) => {
|
||||
let pat = self.collect_pat_opt(stmt.pat());
|
||||
let type_ref = stmt.ascribed_type().map(TypeRef::from_ast);
|
||||
let type_ref = stmt.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx, it));
|
||||
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
|
||||
Statement::Let { pat, type_ref, initializer }
|
||||
}
|
||||
|
@ -491,7 +520,7 @@ impl ExprCollector<'_> {
|
|||
self.alloc_expr(Expr::Block { statements, tail }, syntax_node_ptr)
|
||||
}
|
||||
|
||||
fn collect_block_items(&mut self, block: &ast::Block) {
|
||||
fn collect_block_items(&mut self, block: &ast::BlockExpr) {
|
||||
let container = ContainerId::DefWithBodyId(self.def);
|
||||
for item in block.items() {
|
||||
let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
|
||||
|
|
|
@ -15,6 +15,7 @@ use ra_syntax::ast::{
|
|||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
body::LowerCtx,
|
||||
db::DefDatabase,
|
||||
path::{path, AssociatedTypeBinding, GenericArgs, Path},
|
||||
src::HasSource,
|
||||
|
@ -40,13 +41,14 @@ impl FunctionData {
|
|||
pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> {
|
||||
let loc = func.lookup(db);
|
||||
let src = loc.source(db);
|
||||
let ctx = LowerCtx::new(db, src.file_id);
|
||||
let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing);
|
||||
let mut params = Vec::new();
|
||||
let mut has_self_param = false;
|
||||
if let Some(param_list) = src.value.param_list() {
|
||||
if let Some(self_param) = param_list.self_param() {
|
||||
let self_type = if let Some(type_ref) = self_param.ascribed_type() {
|
||||
TypeRef::from_ast(type_ref)
|
||||
TypeRef::from_ast(&ctx, type_ref)
|
||||
} else {
|
||||
let self_type = TypeRef::Path(name![Self].into());
|
||||
match self_param.kind() {
|
||||
|
@ -63,14 +65,14 @@ impl FunctionData {
|
|||
has_self_param = true;
|
||||
}
|
||||
for param in param_list.params() {
|
||||
let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
|
||||
let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type());
|
||||
params.push(type_ref);
|
||||
}
|
||||
}
|
||||
let attrs = Attrs::new(&src.value, &Hygiene::new(db.upcast(), src.file_id));
|
||||
|
||||
let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) {
|
||||
TypeRef::from_ast(type_ref)
|
||||
TypeRef::from_ast(&ctx, type_ref)
|
||||
} else {
|
||||
TypeRef::unit()
|
||||
};
|
||||
|
@ -122,7 +124,8 @@ impl TypeAliasData {
|
|||
let loc = typ.lookup(db);
|
||||
let node = loc.source(db);
|
||||
let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||
let type_ref = node.value.type_ref().map(TypeRef::from_ast);
|
||||
let lower_ctx = LowerCtx::new(db, node.file_id);
|
||||
let type_ref = node.value.type_ref().map(|it| TypeRef::from_ast(&lower_ctx, it));
|
||||
let vis_default = RawVisibility::default_for_container(loc.container);
|
||||
let visibility = RawVisibility::from_ast_with_default(
|
||||
db,
|
||||
|
@ -130,7 +133,7 @@ impl TypeAliasData {
|
|||
node.as_ref().map(|n| n.visibility()),
|
||||
);
|
||||
let bounds = if let Some(bound_list) = node.value.type_bound_list() {
|
||||
bound_list.bounds().map(TypeBound::from_ast).collect()
|
||||
bound_list.bounds().map(|it| TypeBound::from_ast(&lower_ctx, it)).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
@ -223,9 +226,10 @@ impl ImplData {
|
|||
let _p = profile("impl_data_query");
|
||||
let impl_loc = id.lookup(db);
|
||||
let src = impl_loc.source(db);
|
||||
let lower_ctx = LowerCtx::new(db, src.file_id);
|
||||
|
||||
let target_trait = src.value.target_trait().map(TypeRef::from_ast);
|
||||
let target_type = TypeRef::from_ast_opt(src.value.target_type());
|
||||
let target_trait = src.value.target_trait().map(|it| TypeRef::from_ast(&lower_ctx, it));
|
||||
let target_type = TypeRef::from_ast_opt(&lower_ctx, src.value.target_type());
|
||||
let is_negative = src.value.excl_token().is_some();
|
||||
let module_id = impl_loc.container.module(db);
|
||||
|
||||
|
@ -279,8 +283,9 @@ impl ConstData {
|
|||
vis_default: RawVisibility,
|
||||
node: InFile<N>,
|
||||
) -> ConstData {
|
||||
let ctx = LowerCtx::new(db, node.file_id);
|
||||
let name = node.value.name().map(|n| n.as_name());
|
||||
let type_ref = TypeRef::from_ast_opt(node.value.ascribed_type());
|
||||
let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type());
|
||||
let visibility =
|
||||
RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
|
||||
ConstData { name, type_ref, visibility }
|
||||
|
|
|
@ -15,6 +15,7 @@ use ra_prof::profile;
|
|||
use ra_syntax::ast::{self, NameOwner, TypeBoundsOwner, TypeParamsOwner};
|
||||
|
||||
use crate::{
|
||||
body::LowerCtx,
|
||||
child_by_source::ChildBySource,
|
||||
db::DefDatabase,
|
||||
dyn_map::DynMap,
|
||||
|
@ -80,11 +81,13 @@ impl GenericParams {
|
|||
fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
|
||||
let mut generics = GenericParams { types: Arena::default(), where_predicates: Vec::new() };
|
||||
let mut sm = ArenaMap::default();
|
||||
|
||||
// FIXME: add `: Sized` bound for everything except for `Self` in traits
|
||||
let file_id = match def {
|
||||
GenericDefId::FunctionId(it) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
let lower_ctx = LowerCtx::new(db, src.file_id);
|
||||
generics.fill(&lower_ctx, &mut sm, &src.value);
|
||||
// lower `impl Trait` in arguments
|
||||
let data = db.function_data(it);
|
||||
for param in &data.params {
|
||||
|
@ -94,21 +97,25 @@ impl GenericParams {
|
|||
}
|
||||
GenericDefId::AdtId(AdtId::StructId(it)) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
let lower_ctx = LowerCtx::new(db, src.file_id);
|
||||
generics.fill(&lower_ctx, &mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::UnionId(it)) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
let lower_ctx = LowerCtx::new(db, src.file_id);
|
||||
generics.fill(&lower_ctx, &mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::EnumId(it)) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
let lower_ctx = LowerCtx::new(db, src.file_id);
|
||||
generics.fill(&lower_ctx, &mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::TraitId(it) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
let lower_ctx = LowerCtx::new(db, src.file_id);
|
||||
|
||||
// traits get the Self type as an implicit first type parameter
|
||||
let self_param_id = generics.types.alloc(TypeParamData {
|
||||
|
@ -120,14 +127,16 @@ impl GenericParams {
|
|||
// add super traits as bounds on Self
|
||||
// i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
|
||||
let self_param = TypeRef::Path(name![Self].into());
|
||||
generics.fill_bounds(&src.value, self_param);
|
||||
generics.fill_bounds(&lower_ctx, &src.value, self_param);
|
||||
|
||||
generics.fill(&mut sm, &src.value);
|
||||
generics.fill(&lower_ctx, &mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::TypeAliasId(it) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
let lower_ctx = LowerCtx::new(db, src.file_id);
|
||||
|
||||
generics.fill(&lower_ctx, &mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
// Note that we don't add `Self` here: in `impl`s, `Self` is not a
|
||||
|
@ -135,7 +144,9 @@ impl GenericParams {
|
|||
// type, so this is handled by the resolver.
|
||||
GenericDefId::ImplId(it) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
let lower_ctx = LowerCtx::new(db, src.file_id);
|
||||
|
||||
generics.fill(&lower_ctx, &mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
// We won't be using this ID anyway
|
||||
|
@ -145,28 +156,38 @@ impl GenericParams {
|
|||
(generics, InFile::new(file_id, sm))
|
||||
}
|
||||
|
||||
fn fill(&mut self, sm: &mut SourceMap, node: &dyn TypeParamsOwner) {
|
||||
fn fill(&mut self, lower_ctx: &LowerCtx, sm: &mut SourceMap, node: &dyn TypeParamsOwner) {
|
||||
if let Some(params) = node.type_param_list() {
|
||||
self.fill_params(sm, params)
|
||||
self.fill_params(lower_ctx, sm, params)
|
||||
}
|
||||
if let Some(where_clause) = node.where_clause() {
|
||||
self.fill_where_predicates(where_clause);
|
||||
self.fill_where_predicates(lower_ctx, where_clause);
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_bounds(&mut self, node: &dyn ast::TypeBoundsOwner, type_ref: TypeRef) {
|
||||
fn fill_bounds(
|
||||
&mut self,
|
||||
lower_ctx: &LowerCtx,
|
||||
node: &dyn ast::TypeBoundsOwner,
|
||||
type_ref: TypeRef,
|
||||
) {
|
||||
for bound in
|
||||
node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
|
||||
{
|
||||
self.add_where_predicate_from_bound(bound, type_ref.clone());
|
||||
self.add_where_predicate_from_bound(lower_ctx, bound, type_ref.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_params(&mut self, sm: &mut SourceMap, params: ast::TypeParamList) {
|
||||
fn fill_params(
|
||||
&mut self,
|
||||
lower_ctx: &LowerCtx,
|
||||
sm: &mut SourceMap,
|
||||
params: ast::TypeParamList,
|
||||
) {
|
||||
for type_param in params.type_params() {
|
||||
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
|
||||
// FIXME: Use `Path::from_src`
|
||||
let default = type_param.default_type().map(TypeRef::from_ast);
|
||||
let default = type_param.default_type().map(|it| TypeRef::from_ast(lower_ctx, it));
|
||||
let param = TypeParamData {
|
||||
name: Some(name.clone()),
|
||||
default,
|
||||
|
@ -176,29 +197,34 @@ impl GenericParams {
|
|||
sm.insert(param_id, Either::Right(type_param.clone()));
|
||||
|
||||
let type_ref = TypeRef::Path(name.into());
|
||||
self.fill_bounds(&type_param, type_ref);
|
||||
self.fill_bounds(&lower_ctx, &type_param, type_ref);
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_where_predicates(&mut self, where_clause: ast::WhereClause) {
|
||||
fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) {
|
||||
for pred in where_clause.predicates() {
|
||||
let type_ref = match pred.type_ref() {
|
||||
Some(type_ref) => type_ref,
|
||||
None => continue,
|
||||
};
|
||||
let type_ref = TypeRef::from_ast(type_ref);
|
||||
let type_ref = TypeRef::from_ast(lower_ctx, type_ref);
|
||||
for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
|
||||
self.add_where_predicate_from_bound(bound, type_ref.clone());
|
||||
self.add_where_predicate_from_bound(lower_ctx, bound, type_ref.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_where_predicate_from_bound(&mut self, bound: ast::TypeBound, type_ref: TypeRef) {
|
||||
fn add_where_predicate_from_bound(
|
||||
&mut self,
|
||||
lower_ctx: &LowerCtx,
|
||||
bound: ast::TypeBound,
|
||||
type_ref: TypeRef,
|
||||
) {
|
||||
if bound.question_token().is_some() {
|
||||
// FIXME: remove this bound
|
||||
return;
|
||||
}
|
||||
let bound = TypeBound::from_ast(bound);
|
||||
let bound = TypeBound::from_ast(lower_ctx, bound);
|
||||
self.where_predicates
|
||||
.push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound });
|
||||
}
|
||||
|
|
|
@ -204,6 +204,7 @@ impl DefCollector<'_> {
|
|||
ast_id: None,
|
||||
krate: Some(krate),
|
||||
kind: MacroDefKind::CustomDerive(expander),
|
||||
local_inner: false,
|
||||
};
|
||||
|
||||
self.define_proc_macro(name.clone(), macro_id);
|
||||
|
@ -941,6 +942,7 @@ impl ModCollector<'_, '_> {
|
|||
ast_id: Some(ast_id.ast_id),
|
||||
krate: Some(self.def_collector.def_map.krate),
|
||||
kind: MacroDefKind::Declarative,
|
||||
local_inner: mac.local_inner,
|
||||
};
|
||||
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
|
||||
}
|
||||
|
|
|
@ -188,6 +188,7 @@ pub(super) struct MacroData {
|
|||
pub(super) path: ModPath,
|
||||
pub(super) name: Option<Name>,
|
||||
pub(super) export: bool,
|
||||
pub(super) local_inner: bool,
|
||||
pub(super) builtin: bool,
|
||||
}
|
||||
|
||||
|
@ -401,14 +402,32 @@ impl RawItemsCollector {
|
|||
|
||||
let name = m.name().map(|it| it.as_name());
|
||||
let ast_id = self.source_ast_id_map.ast_id(&m);
|
||||
// FIXME: cfg_attr
|
||||
let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export");
|
||||
|
||||
// FIXME: cfg_attr
|
||||
let builtin =
|
||||
m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "rustc_builtin_macro");
|
||||
let export_attr = attrs.by_key("macro_export");
|
||||
|
||||
let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export, builtin });
|
||||
let export = export_attr.exists();
|
||||
let local_inner = if export {
|
||||
export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
||||
ident.text.contains("local_inner_macros")
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let builtin = attrs.by_key("rustc_builtin_macro").exists();
|
||||
|
||||
let m = self.raw_items.macros.alloc(MacroData {
|
||||
ast_id,
|
||||
path,
|
||||
name,
|
||||
export,
|
||||
local_inner,
|
||||
builtin,
|
||||
});
|
||||
self.push_item(current_module, attrs, RawItemKind::Macro(m));
|
||||
}
|
||||
|
||||
|
|
|
@ -135,6 +135,43 @@ fn macro_rules_export_with_local_inner_macros_are_visible() {
|
|||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_inner_macros_makes_local_macros_usable() {
|
||||
let map = def_map(
|
||||
"
|
||||
//- /main.rs crate:main deps:foo
|
||||
foo::structs!(Foo, Bar);
|
||||
mod bar;
|
||||
//- /bar.rs
|
||||
use crate::*;
|
||||
//- /lib.rs crate:foo
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! structs {
|
||||
($($i:ident),*) => {
|
||||
inner!($($i),*);
|
||||
}
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! inner {
|
||||
($($i:ident),*) => {
|
||||
$(struct $i { field: u32 } )*
|
||||
}
|
||||
}
|
||||
",
|
||||
);
|
||||
assert_snapshot!(map, @r###"
|
||||
⋮crate
|
||||
⋮Bar: t v
|
||||
⋮Foo: t v
|
||||
⋮bar: t
|
||||
⋮
|
||||
⋮crate::bar
|
||||
⋮Bar: t v
|
||||
⋮Foo: t v
|
||||
⋮bar: t
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
||||
let map = def_map(
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::body::LowerCtx;
|
||||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
name::{AsName, Name},
|
||||
|
@ -244,8 +245,8 @@ impl<'a> PathSegments<'a> {
|
|||
}
|
||||
|
||||
impl GenericArgs {
|
||||
pub(crate) fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
|
||||
lower::lower_generic_args(node)
|
||||
pub(crate) fn from_ast(lower_ctx: &LowerCtx, node: ast::TypeArgList) -> Option<GenericArgs> {
|
||||
lower::lower_generic_args(lower_ctx, node)
|
||||
}
|
||||
|
||||
pub(crate) fn empty() -> GenericArgs {
|
||||
|
|
|
@ -13,6 +13,7 @@ use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner, TypeBoundsOwner};
|
|||
|
||||
use super::AssociatedTypeBinding;
|
||||
use crate::{
|
||||
body::LowerCtx,
|
||||
path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
};
|
||||
|
@ -26,6 +27,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
|
|||
let mut type_anchor = None;
|
||||
let mut segments = Vec::new();
|
||||
let mut generic_args = Vec::new();
|
||||
let ctx = LowerCtx::with_hygiene(hygiene);
|
||||
loop {
|
||||
let segment = path.segment()?;
|
||||
|
||||
|
@ -40,9 +42,10 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
|
|||
Either::Left(name) => {
|
||||
let args = segment
|
||||
.type_arg_list()
|
||||
.and_then(lower_generic_args)
|
||||
.and_then(|it| lower_generic_args(&ctx, it))
|
||||
.or_else(|| {
|
||||
lower_generic_args_from_fn_path(
|
||||
&ctx,
|
||||
segment.param_list(),
|
||||
segment.ret_type(),
|
||||
)
|
||||
|
@ -60,7 +63,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
|
|||
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
|
||||
let self_type = TypeRef::from_ast(type_ref?);
|
||||
let self_type = TypeRef::from_ast(&ctx, type_ref?);
|
||||
|
||||
match trait_ref {
|
||||
// <T>::foo
|
||||
|
@ -113,6 +116,21 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
|
|||
}
|
||||
segments.reverse();
|
||||
generic_args.reverse();
|
||||
|
||||
// handle local_inner_macros :
|
||||
// Basically, even in rustc it is quite hacky:
|
||||
// https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
|
||||
// We follow what it did anyway :)
|
||||
if segments.len() == 1 && kind == PathKind::Plain {
|
||||
if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
||||
if macro_call.is_bang() {
|
||||
if let Some(crate_id) = hygiene.local_inner_macros() {
|
||||
kind = PathKind::DollarCrate(crate_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mod_path = ModPath { kind, segments };
|
||||
return Some(Path { type_anchor, mod_path, generic_args });
|
||||
|
||||
|
@ -128,10 +146,13 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_generic_args(node: ast::TypeArgList) -> Option<GenericArgs> {
|
||||
pub(super) fn lower_generic_args(
|
||||
lower_ctx: &LowerCtx,
|
||||
node: ast::TypeArgList,
|
||||
) -> Option<GenericArgs> {
|
||||
let mut args = Vec::new();
|
||||
for type_arg in node.type_args() {
|
||||
let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
|
||||
let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.type_ref());
|
||||
args.push(GenericArg::Type(type_ref));
|
||||
}
|
||||
// lifetimes ignored for now
|
||||
|
@ -140,9 +161,9 @@ pub(super) fn lower_generic_args(node: ast::TypeArgList) -> Option<GenericArgs>
|
|||
let assoc_type_arg: ast::AssocTypeArg = assoc_type_arg;
|
||||
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
||||
let name = name_ref.as_name();
|
||||
let type_ref = assoc_type_arg.type_ref().map(TypeRef::from_ast);
|
||||
let type_ref = assoc_type_arg.type_ref().map(|it| TypeRef::from_ast(lower_ctx, it));
|
||||
let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
|
||||
l.bounds().map(TypeBound::from_ast).collect()
|
||||
l.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
@ -159,6 +180,7 @@ pub(super) fn lower_generic_args(node: ast::TypeArgList) -> Option<GenericArgs>
|
|||
/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
|
||||
/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
|
||||
fn lower_generic_args_from_fn_path(
|
||||
ctx: &LowerCtx,
|
||||
params: Option<ast::ParamList>,
|
||||
ret_type: Option<ast::RetType>,
|
||||
) -> Option<GenericArgs> {
|
||||
|
@ -167,14 +189,14 @@ fn lower_generic_args_from_fn_path(
|
|||
if let Some(params) = params {
|
||||
let mut param_types = Vec::new();
|
||||
for param in params.params() {
|
||||
let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
|
||||
let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type());
|
||||
param_types.push(type_ref);
|
||||
}
|
||||
let arg = GenericArg::Type(TypeRef::Tuple(param_types));
|
||||
args.push(arg);
|
||||
}
|
||||
if let Some(ret_type) = ret_type {
|
||||
let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
|
||||
let type_ref = TypeRef::from_ast_opt(&ctx, ret_type.type_ref());
|
||||
bindings.push(AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
type_ref: Some(type_ref),
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use ra_syntax::ast::{self, TypeAscriptionOwner, TypeBoundsOwner};
|
||||
|
||||
use crate::path::Path;
|
||||
use crate::{body::LowerCtx, path::Path};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Mutability {
|
||||
|
@ -64,30 +64,34 @@ pub enum TypeBound {
|
|||
|
||||
impl TypeRef {
|
||||
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
|
||||
pub(crate) fn from_ast(node: ast::TypeRef) -> Self {
|
||||
pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeRef) -> Self {
|
||||
match node {
|
||||
ast::TypeRef::ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
||||
ast::TypeRef::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.type_ref()),
|
||||
ast::TypeRef::TupleType(inner) => {
|
||||
TypeRef::Tuple(inner.fields().map(TypeRef::from_ast).collect())
|
||||
TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
|
||||
}
|
||||
ast::TypeRef::NeverType(..) => TypeRef::Never,
|
||||
ast::TypeRef::PathType(inner) => {
|
||||
// FIXME: Use `Path::from_src`
|
||||
inner.path().and_then(Path::from_ast).map(TypeRef::Path).unwrap_or(TypeRef::Error)
|
||||
inner
|
||||
.path()
|
||||
.and_then(|it| ctx.lower_path(it))
|
||||
.map(TypeRef::Path)
|
||||
.unwrap_or(TypeRef::Error)
|
||||
}
|
||||
ast::TypeRef::PointerType(inner) => {
|
||||
let inner_ty = TypeRef::from_ast_opt(inner.type_ref());
|
||||
let inner_ty = TypeRef::from_ast_opt(&ctx, inner.type_ref());
|
||||
let mutability = Mutability::from_mutable(inner.mut_token().is_some());
|
||||
TypeRef::RawPtr(Box::new(inner_ty), mutability)
|
||||
}
|
||||
ast::TypeRef::ArrayType(inner) => {
|
||||
TypeRef::Array(Box::new(TypeRef::from_ast_opt(inner.type_ref())))
|
||||
TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.type_ref())))
|
||||
}
|
||||
ast::TypeRef::SliceType(inner) => {
|
||||
TypeRef::Slice(Box::new(TypeRef::from_ast_opt(inner.type_ref())))
|
||||
TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.type_ref())))
|
||||
}
|
||||
ast::TypeRef::ReferenceType(inner) => {
|
||||
let inner_ty = TypeRef::from_ast_opt(inner.type_ref());
|
||||
let inner_ty = TypeRef::from_ast_opt(&ctx, inner.type_ref());
|
||||
let mutability = Mutability::from_mutable(inner.mut_token().is_some());
|
||||
TypeRef::Reference(Box::new(inner_ty), mutability)
|
||||
}
|
||||
|
@ -96,10 +100,13 @@ impl TypeRef {
|
|||
let ret_ty = inner
|
||||
.ret_type()
|
||||
.and_then(|rt| rt.type_ref())
|
||||
.map(TypeRef::from_ast)
|
||||
.map(|it| TypeRef::from_ast(ctx, it))
|
||||
.unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
|
||||
let mut params = if let Some(pl) = inner.param_list() {
|
||||
pl.params().map(|p| p.ascribed_type()).map(TypeRef::from_ast_opt).collect()
|
||||
pl.params()
|
||||
.map(|p| p.ascribed_type())
|
||||
.map(|it| TypeRef::from_ast_opt(&ctx, it))
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
@ -107,19 +114,19 @@ impl TypeRef {
|
|||
TypeRef::Fn(params)
|
||||
}
|
||||
// for types are close enough for our purposes to the inner type for now...
|
||||
ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
||||
ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.type_ref()),
|
||||
ast::TypeRef::ImplTraitType(inner) => {
|
||||
TypeRef::ImplTrait(type_bounds_from_ast(inner.type_bound_list()))
|
||||
TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
|
||||
}
|
||||
ast::TypeRef::DynTraitType(inner) => {
|
||||
TypeRef::DynTrait(type_bounds_from_ast(inner.type_bound_list()))
|
||||
TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
|
||||
pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::TypeRef>) -> Self {
|
||||
if let Some(node) = node {
|
||||
TypeRef::from_ast(node)
|
||||
TypeRef::from_ast(ctx, node)
|
||||
} else {
|
||||
TypeRef::Error
|
||||
}
|
||||
|
@ -180,24 +187,27 @@ impl TypeRef {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option<ast::TypeBoundList>) -> Vec<TypeBound> {
|
||||
pub(crate) fn type_bounds_from_ast(
|
||||
lower_ctx: &LowerCtx,
|
||||
type_bounds_opt: Option<ast::TypeBoundList>,
|
||||
) -> Vec<TypeBound> {
|
||||
if let Some(type_bounds) = type_bounds_opt {
|
||||
type_bounds.bounds().map(TypeBound::from_ast).collect()
|
||||
type_bounds.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeBound {
|
||||
pub(crate) fn from_ast(node: ast::TypeBound) -> Self {
|
||||
pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
|
||||
match node.kind() {
|
||||
ast::TypeBoundKind::PathType(path_type) => {
|
||||
let path = match path_type.path() {
|
||||
Some(p) => p,
|
||||
None => return TypeBound::Error,
|
||||
};
|
||||
// FIXME: Use `Path::from_src`
|
||||
let path = match Path::from_ast(path) {
|
||||
|
||||
let path = match ctx.lower_path(path) {
|
||||
Some(p) => p,
|
||||
None => return TypeBound::Error,
|
||||
};
|
||||
|
|
|
@ -38,7 +38,7 @@ macro_rules! register_builtin {
|
|||
_ => return None,
|
||||
};
|
||||
|
||||
Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind) })
|
||||
Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind), local_inner: false })
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -73,11 +73,13 @@ pub fn find_builtin_macro(
|
|||
krate: Some(krate),
|
||||
ast_id: Some(ast_id),
|
||||
kind: MacroDefKind::BuiltIn(kind),
|
||||
local_inner: false,
|
||||
}),
|
||||
Either::Right(kind) => Some(MacroDefId {
|
||||
krate: Some(krate),
|
||||
ast_id: Some(ast_id),
|
||||
kind: MacroDefKind::BuiltInEager(kind),
|
||||
local_inner: false,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -406,6 +408,7 @@ mod tests {
|
|||
krate: Some(CrateId(0)),
|
||||
ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))),
|
||||
kind: MacroDefKind::BuiltIn(expander),
|
||||
local_inner: false,
|
||||
};
|
||||
|
||||
let loc = MacroCallLoc {
|
||||
|
@ -425,6 +428,7 @@ mod tests {
|
|||
krate: Some(CrateId(0)),
|
||||
ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))),
|
||||
kind: MacroDefKind::BuiltInEager(expander),
|
||||
local_inner: false,
|
||||
};
|
||||
|
||||
let args = macro_calls[1].token_tree().unwrap();
|
||||
|
|
|
@ -330,7 +330,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
|
|||
FragmentKind::Expr
|
||||
}
|
||||
// FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that
|
||||
EXPR_STMT | BLOCK => FragmentKind::Expr,
|
||||
EXPR_STMT | BLOCK_EXPR => FragmentKind::Expr,
|
||||
ARG_LIST => FragmentKind::Expr,
|
||||
TRY_EXPR => FragmentKind::Expr,
|
||||
TUPLE_EXPR => FragmentKind::Expr,
|
||||
|
@ -342,7 +342,6 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
|
|||
CONDITION => FragmentKind::Expr,
|
||||
BREAK_EXPR => FragmentKind::Expr,
|
||||
RETURN_EXPR => FragmentKind::Expr,
|
||||
BLOCK_EXPR => FragmentKind::Expr,
|
||||
MATCH_EXPR => FragmentKind::Expr,
|
||||
MATCH_ARM => FragmentKind::Expr,
|
||||
MATCH_GUARD => FragmentKind::Expr,
|
||||
|
|
|
@ -12,35 +12,38 @@ use crate::{
|
|||
HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Hygiene {
|
||||
// This is what `$crate` expands to
|
||||
def_crate: Option<CrateId>,
|
||||
|
||||
// Indiciate this is a local inner macro
|
||||
local_inner: bool,
|
||||
}
|
||||
|
||||
impl Hygiene {
|
||||
pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
|
||||
let def_crate = match file_id.0 {
|
||||
HirFileIdRepr::FileId(_) => None,
|
||||
let (def_crate, local_inner) = match file_id.0 {
|
||||
HirFileIdRepr::FileId(_) => (None, false),
|
||||
HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
|
||||
MacroCallId::LazyMacro(id) => {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
match loc.def.kind {
|
||||
MacroDefKind::Declarative => loc.def.krate,
|
||||
MacroDefKind::BuiltIn(_) => None,
|
||||
MacroDefKind::BuiltInDerive(_) => None,
|
||||
MacroDefKind::BuiltInEager(_) => None,
|
||||
MacroDefKind::CustomDerive(_) => None,
|
||||
MacroDefKind::Declarative => (loc.def.krate, loc.def.local_inner),
|
||||
MacroDefKind::BuiltIn(_) => (None, false),
|
||||
MacroDefKind::BuiltInDerive(_) => (None, false),
|
||||
MacroDefKind::BuiltInEager(_) => (None, false),
|
||||
MacroDefKind::CustomDerive(_) => (None, false),
|
||||
}
|
||||
}
|
||||
MacroCallId::EagerMacro(_id) => None,
|
||||
MacroCallId::EagerMacro(_id) => (None, false),
|
||||
},
|
||||
};
|
||||
Hygiene { def_crate }
|
||||
Hygiene { def_crate, local_inner }
|
||||
}
|
||||
|
||||
pub fn new_unhygienic() -> Hygiene {
|
||||
Hygiene { def_crate: None }
|
||||
Hygiene { def_crate: None, local_inner: false }
|
||||
}
|
||||
|
||||
// FIXME: this should just return name
|
||||
|
@ -52,4 +55,12 @@ impl Hygiene {
|
|||
}
|
||||
Either::Left(name_ref.as_name())
|
||||
}
|
||||
|
||||
pub fn local_inner_macros(&self) -> Option<CrateId> {
|
||||
if self.local_inner {
|
||||
self.def_crate
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,6 +204,8 @@ pub struct MacroDefId {
|
|||
pub krate: Option<CrateId>,
|
||||
pub ast_id: Option<AstId<ast::MacroCall>>,
|
||||
pub kind: MacroDefKind,
|
||||
|
||||
pub local_inner: bool,
|
||||
}
|
||||
|
||||
impl MacroDefId {
|
||||
|
|
|
@ -11,7 +11,7 @@ doctest = false
|
|||
itertools = "0.9.0"
|
||||
arrayvec = "0.5.1"
|
||||
smallvec = "1.2.0"
|
||||
ena = "0.13.1"
|
||||
ena = "0.14.0"
|
||||
log = "0.4.8"
|
||||
rustc-hash = "1.1.0"
|
||||
|
||||
|
|
|
@ -66,7 +66,8 @@ pub use autoderef::autoderef;
|
|||
pub use infer::{InferTy, InferenceResult};
|
||||
pub use lower::CallableDef;
|
||||
pub use lower::{
|
||||
callable_item_sig, ImplTraitLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId,
|
||||
associated_type_shorthand_candidates, callable_item_sig, ImplTraitLoweringMode, TyDefId,
|
||||
TyLoweringContext, ValueTyDefId,
|
||||
};
|
||||
pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment};
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ use hir_def::{
|
|||
path::{GenericArg, Path, PathSegment, PathSegments},
|
||||
resolver::{HasResolver, Resolver, TypeNs},
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
AdtId, AssocContainerId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule,
|
||||
ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
|
||||
VariantId,
|
||||
AdtId, AssocContainerId, AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId,
|
||||
HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
|
||||
UnionId, VariantId,
|
||||
};
|
||||
use ra_arena::map::ArenaMap;
|
||||
use ra_db::CrateId;
|
||||
|
@ -34,6 +34,7 @@ use crate::{
|
|||
Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate,
|
||||
ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TyLoweringContext<'a> {
|
||||
|
@ -383,61 +384,38 @@ impl Ty {
|
|||
res: Option<TypeNs>,
|
||||
segment: PathSegment<'_>,
|
||||
) -> Ty {
|
||||
let traits_from_env: Vec<_> = match res {
|
||||
Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
|
||||
None => return Ty::Unknown,
|
||||
Some(trait_ref) => vec![trait_ref.value],
|
||||
},
|
||||
Some(TypeNs::GenericParam(param_id)) => {
|
||||
let predicates = ctx.db.generic_predicates_for_param(param_id);
|
||||
let mut traits_: Vec<_> = predicates
|
||||
.iter()
|
||||
.filter_map(|pred| match &pred.value {
|
||||
GenericPredicate::Implemented(tr) => Some(tr.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
// Handle `Self::Type` referring to own associated type in trait definitions
|
||||
if let GenericDefId::TraitId(trait_id) = param_id.parent {
|
||||
let generics = generics(ctx.db.upcast(), trait_id.into());
|
||||
if generics.params.types[param_id.local_id].provenance
|
||||
== TypeParamProvenance::TraitSelf
|
||||
{
|
||||
let trait_ref = TraitRef {
|
||||
trait_: trait_id,
|
||||
substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
|
||||
if let Some(res) = res {
|
||||
let ty =
|
||||
associated_type_shorthand_candidates(ctx.db, res, move |name, t, associated_ty| {
|
||||
if name == segment.name {
|
||||
let substs = match ctx.type_param_mode {
|
||||
TypeParamLoweringMode::Placeholder => {
|
||||
// if we're lowering to placeholders, we have to put
|
||||
// them in now
|
||||
let s = Substs::type_params(
|
||||
ctx.db,
|
||||
ctx.resolver.generic_def().expect(
|
||||
"there should be generics if there's a generic param",
|
||||
),
|
||||
);
|
||||
t.substs.clone().subst_bound_vars(&s)
|
||||
}
|
||||
TypeParamLoweringMode::Variable => t.substs.clone(),
|
||||
};
|
||||
traits_.push(trait_ref);
|
||||
// FIXME handle type parameters on the segment
|
||||
return Some(Ty::Projection(ProjectionTy {
|
||||
associated_ty,
|
||||
parameters: substs,
|
||||
}));
|
||||
}
|
||||
}
|
||||
traits_
|
||||
}
|
||||
_ => return Ty::Unknown,
|
||||
};
|
||||
let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t));
|
||||
for t in traits {
|
||||
if let Some(associated_ty) =
|
||||
ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name)
|
||||
{
|
||||
let substs = match ctx.type_param_mode {
|
||||
TypeParamLoweringMode::Placeholder => {
|
||||
// if we're lowering to placeholders, we have to put
|
||||
// them in now
|
||||
let s = Substs::type_params(
|
||||
ctx.db,
|
||||
ctx.resolver
|
||||
.generic_def()
|
||||
.expect("there should be generics if there's a generic param"),
|
||||
);
|
||||
t.substs.subst_bound_vars(&s)
|
||||
}
|
||||
TypeParamLoweringMode::Variable => t.substs,
|
||||
};
|
||||
// FIXME handle (forbid) type parameters on the segment
|
||||
return Ty::Projection(ProjectionTy { associated_ty, parameters: substs });
|
||||
}
|
||||
|
||||
None
|
||||
});
|
||||
|
||||
ty.unwrap_or(Ty::Unknown)
|
||||
} else {
|
||||
Ty::Unknown
|
||||
}
|
||||
Ty::Unknown
|
||||
}
|
||||
|
||||
fn from_hir_path_inner(
|
||||
|
@ -694,6 +672,61 @@ pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn associated_type_shorthand_candidates<R>(
|
||||
db: &dyn HirDatabase,
|
||||
res: TypeNs,
|
||||
mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
|
||||
) -> Option<R> {
|
||||
let traits_from_env: Vec<_> = match res {
|
||||
TypeNs::SelfType(impl_id) => match db.impl_trait(impl_id) {
|
||||
None => vec![],
|
||||
Some(trait_ref) => vec![trait_ref.value],
|
||||
},
|
||||
TypeNs::GenericParam(param_id) => {
|
||||
let predicates = db.generic_predicates_for_param(param_id);
|
||||
let mut traits_: Vec<_> = predicates
|
||||
.iter()
|
||||
.filter_map(|pred| match &pred.value {
|
||||
GenericPredicate::Implemented(tr) => Some(tr.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
// Handle `Self::Type` referring to own associated type in trait definitions
|
||||
if let GenericDefId::TraitId(trait_id) = param_id.parent {
|
||||
let generics = generics(db.upcast(), trait_id.into());
|
||||
if generics.params.types[param_id.local_id].provenance
|
||||
== TypeParamProvenance::TraitSelf
|
||||
{
|
||||
let trait_ref = TraitRef {
|
||||
trait_: trait_id,
|
||||
substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
|
||||
};
|
||||
traits_.push(trait_ref);
|
||||
}
|
||||
}
|
||||
traits_
|
||||
}
|
||||
_ => vec![],
|
||||
};
|
||||
|
||||
for t in traits_from_env.into_iter().flat_map(move |t| all_super_trait_refs(db, t)) {
|
||||
let data = db.trait_data(t.trait_);
|
||||
|
||||
for (name, assoc_id) in &data.items {
|
||||
match assoc_id {
|
||||
AssocItemId::TypeAliasId(alias) => {
|
||||
if let Some(result) = cb(name, &t, *alias) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Build the type of all specific fields of a struct or enum variant.
|
||||
pub(crate) fn field_types_query(
|
||||
db: &dyn HirDatabase,
|
||||
|
|
|
@ -338,6 +338,46 @@ pub fn baz() -> usize { 31usize }
|
|||
assert_eq!("(i32, usize)", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_macro_with_dollar_crate_is_correct_in_trait_associate_type() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:foo
|
||||
use foo::Trait;
|
||||
|
||||
fn test() {
|
||||
let msg = foo::Message(foo::MessageRef);
|
||||
let r = msg.deref();
|
||||
r<|>;
|
||||
}
|
||||
|
||||
//- /lib.rs crate:foo
|
||||
pub struct MessageRef;
|
||||
pub struct Message(MessageRef);
|
||||
|
||||
pub trait Trait {
|
||||
type Target;
|
||||
fn deref(&self) -> &Self::Target;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! expand {
|
||||
() => {
|
||||
impl Trait for Message {
|
||||
type Target = $crate::MessageRef;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expand!();
|
||||
"#,
|
||||
);
|
||||
assert_eq!("&MessageRef", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_type_value_non_legacy_macro_use_as() {
|
||||
assert_snapshot!(
|
||||
|
@ -387,6 +427,32 @@ fn main() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_local_inner_macros() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:foo
|
||||
fn test() {
|
||||
let x = foo::foo!(1);
|
||||
x<|>;
|
||||
}
|
||||
|
||||
//- /lib.rs crate:foo
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! foo {
|
||||
(1) => { bar!() };
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bar {
|
||||
() => { 42 }
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
assert_eq!("i32", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_builtin_macros_line() {
|
||||
assert_snapshot!(
|
||||
|
|
|
@ -1755,3 +1755,35 @@ fn main() {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effects_smoke_test() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn main() {
|
||||
let x = unsafe { 92 };
|
||||
let y = async { async { () }.await };
|
||||
let z = try { () };
|
||||
let t = 'a: { 92 };
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
11..131 '{ ...2 }; }': ()
|
||||
21..22 'x': i32
|
||||
32..38 '{ 92 }': i32
|
||||
34..36 '92': i32
|
||||
48..49 'y': {unknown}
|
||||
58..80 '{ asyn...wait }': {unknown}
|
||||
60..78 'async ....await': {unknown}
|
||||
66..72 '{ () }': ()
|
||||
68..70 '()': ()
|
||||
90..91 'z': {unknown}
|
||||
94..104 'try { () }': {unknown}
|
||||
98..104 '{ () }': ()
|
||||
100..102 '()': ()
|
||||
114..115 't': i32
|
||||
122..128 '{ 92 }': i32
|
||||
124..126 '92': i32
|
||||
"###
|
||||
)
|
||||
}
|
||||
|
|
|
@ -249,6 +249,44 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_union_field_completion() {
|
||||
assert_debug_snapshot!(
|
||||
do_ref_completion(
|
||||
r"
|
||||
union Un {
|
||||
field: u8,
|
||||
other: u16,
|
||||
}
|
||||
|
||||
fn foo(u: Un) {
|
||||
u.<|>
|
||||
}
|
||||
",
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "field",
|
||||
source_range: 140..140,
|
||||
delete: 140..140,
|
||||
insert: "field",
|
||||
kind: Field,
|
||||
detail: "u8",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "other",
|
||||
source_range: 140..140,
|
||||
delete: 140..140,
|
||||
insert: "other",
|
||||
kind: Field,
|
||||
detail: "u16",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_completion() {
|
||||
assert_debug_snapshot!(
|
||||
|
|
|
@ -5,19 +5,29 @@ use ra_syntax::AstNode;
|
|||
use test_utils::tested_by;
|
||||
|
||||
use crate::completion::{CompletionContext, Completions};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
let path = match &ctx.path_prefix {
|
||||
Some(path) => path.clone(),
|
||||
_ => return,
|
||||
};
|
||||
let def = match ctx.scope().resolve_hir_path(&path) {
|
||||
Some(PathResolution::Def(def)) => def,
|
||||
_ => return,
|
||||
let scope = ctx.scope();
|
||||
let context_module = scope.module();
|
||||
|
||||
let res = match scope.resolve_hir_path(&path) {
|
||||
Some(res) => res,
|
||||
None => return,
|
||||
};
|
||||
let context_module = ctx.scope().module();
|
||||
match def {
|
||||
hir::ModuleDef::Module(module) => {
|
||||
|
||||
// Add associated types on type parameters and `Self`.
|
||||
res.assoc_type_shorthand_candidates(ctx.db, |alias| {
|
||||
acc.add_type_alias(ctx, alias);
|
||||
None::<()>
|
||||
});
|
||||
|
||||
match res {
|
||||
PathResolution::Def(hir::ModuleDef::Module(module)) => {
|
||||
let module_scope = module.scope(ctx.db, context_module);
|
||||
for (name, def) in module_scope {
|
||||
if ctx.use_item_syntax.is_some() {
|
||||
|
@ -35,7 +45,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
acc.add_resolution(ctx, name.to_string(), &def);
|
||||
}
|
||||
}
|
||||
hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => {
|
||||
PathResolution::Def(def @ hir::ModuleDef::Adt(_))
|
||||
| PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => {
|
||||
if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
|
||||
for variant in e.variants(ctx.db) {
|
||||
acc.add_enum_variant(ctx, variant, None);
|
||||
|
@ -46,8 +57,10 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// Iterate assoc types separately
|
||||
// FIXME: complete T::AssocType
|
||||
|
||||
// XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
|
||||
// (where AssocType is defined on a trait, not an inherent impl)
|
||||
|
||||
let krate = ctx.krate;
|
||||
if let Some(krate) = krate {
|
||||
let traits_in_scope = ctx.scope().traits_in_scope();
|
||||
|
@ -65,6 +78,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
None::<()>
|
||||
});
|
||||
|
||||
// Iterate assoc types separately
|
||||
ty.iterate_impl_items(ctx.db, krate, |item| {
|
||||
if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
|
||||
return None;
|
||||
|
@ -77,7 +91,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
});
|
||||
}
|
||||
}
|
||||
hir::ModuleDef::Trait(t) => {
|
||||
PathResolution::Def(hir::ModuleDef::Trait(t)) => {
|
||||
// Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
|
||||
for item in t.items(ctx.db) {
|
||||
if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
|
||||
continue;
|
||||
|
@ -91,8 +106,38 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
}
|
||||
}
|
||||
}
|
||||
PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
|
||||
if let Some(krate) = ctx.krate {
|
||||
let ty = match res {
|
||||
PathResolution::TypeParam(param) => param.ty(ctx.db),
|
||||
PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let traits_in_scope = ctx.scope().traits_in_scope();
|
||||
let mut seen = FxHashSet::default();
|
||||
ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
|
||||
if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// We might iterate candidates of a trait multiple times here, so deduplicate
|
||||
// them.
|
||||
if seen.insert(item) {
|
||||
match item {
|
||||
hir::AssocItem::Function(func) => {
|
||||
acc.add_function(ctx, func, None);
|
||||
}
|
||||
hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
|
||||
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
||||
}
|
||||
}
|
||||
None::<()>
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -843,6 +888,211 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_ty_param_assoc_ty() {
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
"
|
||||
//- /lib.rs
|
||||
trait Super {
|
||||
type Ty;
|
||||
const CONST: u8;
|
||||
fn func() {}
|
||||
fn method(&self) {}
|
||||
}
|
||||
|
||||
trait Sub: Super {
|
||||
type SubTy;
|
||||
const C2: ();
|
||||
fn subfunc() {}
|
||||
fn submethod(&self) {}
|
||||
}
|
||||
|
||||
fn foo<T: Sub>() {
|
||||
T::<|>
|
||||
}
|
||||
"
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "C2",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "C2",
|
||||
kind: Const,
|
||||
detail: "const C2: ();",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "CONST",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "CONST",
|
||||
kind: Const,
|
||||
detail: "const CONST: u8;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "SubTy",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "SubTy",
|
||||
kind: TypeAlias,
|
||||
detail: "type SubTy;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "Ty",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "Ty",
|
||||
kind: TypeAlias,
|
||||
detail: "type Ty;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "func()",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "func()$0",
|
||||
kind: Function,
|
||||
lookup: "func",
|
||||
detail: "fn func()",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "method()",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "method()$0",
|
||||
kind: Method,
|
||||
lookup: "method",
|
||||
detail: "fn method(&self)",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "subfunc()",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "subfunc()$0",
|
||||
kind: Function,
|
||||
lookup: "subfunc",
|
||||
detail: "fn subfunc()",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "submethod()",
|
||||
source_range: 219..219,
|
||||
delete: 219..219,
|
||||
insert: "submethod()$0",
|
||||
kind: Method,
|
||||
lookup: "submethod",
|
||||
detail: "fn submethod(&self)",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_self_param_assoc_ty() {
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
"
|
||||
//- /lib.rs
|
||||
trait Super {
|
||||
type Ty;
|
||||
const CONST: u8 = 0;
|
||||
fn func() {}
|
||||
fn method(&self) {}
|
||||
}
|
||||
|
||||
trait Sub: Super {
|
||||
type SubTy;
|
||||
const C2: () = ();
|
||||
fn subfunc() {}
|
||||
fn submethod(&self) {}
|
||||
}
|
||||
|
||||
struct Wrap<T>(T);
|
||||
impl<T> Super for Wrap<T> {}
|
||||
impl<T> Sub for Wrap<T> {
|
||||
fn subfunc() {
|
||||
// Should be able to assume `Self: Sub + Super`
|
||||
Self::<|>
|
||||
}
|
||||
}
|
||||
"
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "C2",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "C2",
|
||||
kind: Const,
|
||||
detail: "const C2: () = ();",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "CONST",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "CONST",
|
||||
kind: Const,
|
||||
detail: "const CONST: u8 = 0;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "SubTy",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "SubTy",
|
||||
kind: TypeAlias,
|
||||
detail: "type SubTy;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "Ty",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "Ty",
|
||||
kind: TypeAlias,
|
||||
detail: "type Ty;",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "func()",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "func()$0",
|
||||
kind: Function,
|
||||
lookup: "func",
|
||||
detail: "fn func()",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "method()",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "method()$0",
|
||||
kind: Method,
|
||||
lookup: "method",
|
||||
detail: "fn method(&self)",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "subfunc()",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "subfunc()$0",
|
||||
kind: Function,
|
||||
lookup: "subfunc",
|
||||
detail: "fn subfunc()",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "submethod()",
|
||||
source_range: 365..365,
|
||||
delete: 365..365,
|
||||
insert: "submethod()$0",
|
||||
kind: Method,
|
||||
lookup: "submethod",
|
||||
detail: "fn submethod(&self)",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_type_alias() {
|
||||
assert_debug_snapshot!(
|
||||
|
|
|
@ -53,7 +53,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
|
|||
// Variants with trivial paths are already added by the existing completion logic,
|
||||
// so we should avoid adding these twice
|
||||
if path.segments.len() > 1 {
|
||||
acc.add_enum_variant(ctx, variant, Some(path.to_string()));
|
||||
acc.add_qualified_enum_variant(ctx, variant, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1173,6 +1173,7 @@ mod tests {
|
|||
delete: 248..250,
|
||||
insert: "Foo::Bar",
|
||||
kind: EnumVariant,
|
||||
lookup: "Bar",
|
||||
detail: "()",
|
||||
},
|
||||
CompletionItem {
|
||||
|
@ -1181,6 +1182,7 @@ mod tests {
|
|||
delete: 248..250,
|
||||
insert: "Foo::Baz",
|
||||
kind: EnumVariant,
|
||||
lookup: "Baz",
|
||||
detail: "()",
|
||||
},
|
||||
CompletionItem {
|
||||
|
@ -1189,6 +1191,7 @@ mod tests {
|
|||
delete: 248..250,
|
||||
insert: "Foo::Quux",
|
||||
kind: EnumVariant,
|
||||
lookup: "Quux",
|
||||
detail: "()",
|
||||
},
|
||||
]
|
||||
|
@ -1231,6 +1234,7 @@ mod tests {
|
|||
delete: 219..221,
|
||||
insert: "Foo::Bar",
|
||||
kind: EnumVariant,
|
||||
lookup: "Bar",
|
||||
detail: "()",
|
||||
},
|
||||
CompletionItem {
|
||||
|
@ -1239,6 +1243,7 @@ mod tests {
|
|||
delete: 219..221,
|
||||
insert: "Foo::Baz",
|
||||
kind: EnumVariant,
|
||||
lookup: "Baz",
|
||||
detail: "()",
|
||||
},
|
||||
CompletionItem {
|
||||
|
@ -1247,6 +1252,7 @@ mod tests {
|
|||
delete: 219..221,
|
||||
insert: "Foo::Quux",
|
||||
kind: EnumVariant,
|
||||
lookup: "Quux",
|
||||
detail: "()",
|
||||
},
|
||||
]
|
||||
|
@ -1285,6 +1291,7 @@ mod tests {
|
|||
delete: 185..186,
|
||||
insert: "Foo::Bar",
|
||||
kind: EnumVariant,
|
||||
lookup: "Bar",
|
||||
detail: "()",
|
||||
},
|
||||
CompletionItem {
|
||||
|
@ -1293,6 +1300,7 @@ mod tests {
|
|||
delete: 185..186,
|
||||
insert: "Foo::Baz",
|
||||
kind: EnumVariant,
|
||||
lookup: "Baz",
|
||||
detail: "()",
|
||||
},
|
||||
CompletionItem {
|
||||
|
@ -1301,6 +1309,7 @@ mod tests {
|
|||
delete: 185..186,
|
||||
insert: "Foo::Quux",
|
||||
kind: EnumVariant,
|
||||
lookup: "Quux",
|
||||
detail: "()",
|
||||
},
|
||||
CompletionItem {
|
||||
|
@ -1353,6 +1362,7 @@ mod tests {
|
|||
delete: 98..99,
|
||||
insert: "m::E::V",
|
||||
kind: EnumVariant,
|
||||
lookup: "V",
|
||||
detail: "()",
|
||||
},
|
||||
]
|
||||
|
|
|
@ -344,7 +344,7 @@ impl<'a> CompletionContext<'a> {
|
|||
stmt.syntax().text_range() == name_ref.syntax().text_range(),
|
||||
);
|
||||
}
|
||||
if let Some(block) = ast::Block::cast(node) {
|
||||
if let Some(block) = ast::BlockExpr::cast(node) {
|
||||
return Some(
|
||||
block.expr().map(|e| e.syntax().text_range())
|
||||
== Some(name_ref.syntax().text_range()),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! This modules takes care of rendering various definitions as completion items.
|
||||
|
||||
use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type};
|
||||
use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
|
||||
use ra_syntax::ast::NameOwner;
|
||||
use stdx::SepBy;
|
||||
use test_utils::tested_by;
|
||||
|
@ -246,14 +246,37 @@ impl Completions {
|
|||
.add_to(self);
|
||||
}
|
||||
|
||||
pub(crate) fn add_qualified_enum_variant(
|
||||
&mut self,
|
||||
ctx: &CompletionContext,
|
||||
variant: hir::EnumVariant,
|
||||
path: ModPath,
|
||||
) {
|
||||
self.add_enum_variant_impl(ctx, variant, None, Some(path))
|
||||
}
|
||||
|
||||
pub(crate) fn add_enum_variant(
|
||||
&mut self,
|
||||
ctx: &CompletionContext,
|
||||
variant: hir::EnumVariant,
|
||||
local_name: Option<String>,
|
||||
) {
|
||||
self.add_enum_variant_impl(ctx, variant, local_name, None)
|
||||
}
|
||||
|
||||
fn add_enum_variant_impl(
|
||||
&mut self,
|
||||
ctx: &CompletionContext,
|
||||
variant: hir::EnumVariant,
|
||||
local_name: Option<String>,
|
||||
path: Option<ModPath>,
|
||||
) {
|
||||
let is_deprecated = is_deprecated(variant, ctx.db);
|
||||
let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string());
|
||||
let qualified_name = match &path {
|
||||
Some(it) => it.to_string(),
|
||||
None => name.to_string(),
|
||||
};
|
||||
let detail_types = variant
|
||||
.fields(ctx.db)
|
||||
.into_iter()
|
||||
|
@ -271,16 +294,23 @@ impl Completions {
|
|||
.surround_with("{ ", " }")
|
||||
.to_string(),
|
||||
};
|
||||
let mut res =
|
||||
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
|
||||
.kind(CompletionItemKind::EnumVariant)
|
||||
.set_documentation(variant.docs(ctx.db))
|
||||
.set_deprecated(is_deprecated)
|
||||
.detail(detail);
|
||||
let mut res = CompletionItem::new(
|
||||
CompletionKind::Reference,
|
||||
ctx.source_range(),
|
||||
qualified_name.clone(),
|
||||
)
|
||||
.kind(CompletionItemKind::EnumVariant)
|
||||
.set_documentation(variant.docs(ctx.db))
|
||||
.set_deprecated(is_deprecated)
|
||||
.detail(detail);
|
||||
|
||||
if path.is_some() {
|
||||
res = res.lookup_by(name);
|
||||
}
|
||||
|
||||
if variant_kind == StructKind::Tuple {
|
||||
let params = Params::Anonymous(variant.fields(ctx.db).len());
|
||||
res = res.add_call_parens(ctx, name, params)
|
||||
res = res.add_call_parens(ctx, qualified_name, params)
|
||||
}
|
||||
|
||||
res.add_to(self);
|
||||
|
|
|
@ -26,6 +26,8 @@ pub struct FunctionSignature {
|
|||
pub kind: CallableKind,
|
||||
/// Optional visibility
|
||||
pub visibility: Option<String>,
|
||||
/// Qualifiers like `async`, `unsafe`, ...
|
||||
pub qualifier: FunctionQualifier,
|
||||
/// Name of the function
|
||||
pub name: Option<String>,
|
||||
/// Documentation for the function
|
||||
|
@ -46,6 +48,16 @@ pub struct FunctionSignature {
|
|||
pub has_self_param: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FunctionQualifier {
|
||||
// `async` and `const` are mutually exclusive. Do we need to enforcing it here?
|
||||
pub is_async: bool,
|
||||
pub is_const: bool,
|
||||
pub is_unsafe: bool,
|
||||
/// The string `extern ".."`
|
||||
pub extern_abi: Option<String>,
|
||||
}
|
||||
|
||||
impl FunctionSignature {
|
||||
pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
|
||||
self.doc = doc;
|
||||
|
@ -83,6 +95,8 @@ impl FunctionSignature {
|
|||
FunctionSignature {
|
||||
kind: CallableKind::StructConstructor,
|
||||
visibility: node.visibility().map(|n| n.syntax().text().to_string()),
|
||||
// Do we need `const`?
|
||||
qualifier: Default::default(),
|
||||
name: node.name().map(|n| n.text().to_string()),
|
||||
ret_type: node.name().map(|n| n.text().to_string()),
|
||||
parameters: params,
|
||||
|
@ -128,6 +142,8 @@ impl FunctionSignature {
|
|||
FunctionSignature {
|
||||
kind: CallableKind::VariantConstructor,
|
||||
visibility: None,
|
||||
// Do we need `const`?
|
||||
qualifier: Default::default(),
|
||||
name: Some(name),
|
||||
ret_type: None,
|
||||
parameters: params,
|
||||
|
@ -151,6 +167,7 @@ impl FunctionSignature {
|
|||
FunctionSignature {
|
||||
kind: CallableKind::Macro,
|
||||
visibility: None,
|
||||
qualifier: Default::default(),
|
||||
name: node.name().map(|n| n.text().to_string()),
|
||||
ret_type: None,
|
||||
parameters: params,
|
||||
|
@ -223,6 +240,12 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
|
|||
FunctionSignature {
|
||||
kind: CallableKind::Function,
|
||||
visibility: node.visibility().map(|n| n.syntax().text().to_string()),
|
||||
qualifier: FunctionQualifier {
|
||||
is_async: node.async_token().is_some(),
|
||||
is_const: node.const_token().is_some(),
|
||||
is_unsafe: node.unsafe_token().is_some(),
|
||||
extern_abi: node.abi().map(|n| n.to_string()),
|
||||
},
|
||||
name: node.name().map(|n| n.text().to_string()),
|
||||
ret_type: node
|
||||
.ret_type()
|
||||
|
@ -246,6 +269,23 @@ impl Display for FunctionSignature {
|
|||
write!(f, "{} ", t)?;
|
||||
}
|
||||
|
||||
if self.qualifier.is_async {
|
||||
write!(f, "async ")?;
|
||||
}
|
||||
|
||||
if self.qualifier.is_const {
|
||||
write!(f, "const ")?;
|
||||
}
|
||||
|
||||
if self.qualifier.is_unsafe {
|
||||
write!(f, "unsafe ")?;
|
||||
}
|
||||
|
||||
if let Some(extern_abi) = &self.qualifier.extern_abi {
|
||||
// Keyword `extern` is included in the string.
|
||||
write!(f, "{} ", extern_abi)?;
|
||||
}
|
||||
|
||||
if let Some(name) = &self.name {
|
||||
match self.kind {
|
||||
CallableKind::Function => write!(f, "fn {}", name)?,
|
||||
|
|
|
@ -33,7 +33,11 @@ impl ShortLabel for ast::EnumDef {
|
|||
|
||||
impl ShortLabel for ast::TraitDef {
|
||||
fn short_label(&self) -> Option<String> {
|
||||
short_label_from_node(self, "trait ")
|
||||
if self.unsafe_token().is_some() {
|
||||
short_label_from_node(self, "unsafe trait ")
|
||||
} else {
|
||||
short_label_from_node(self, "trait ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
|
|||
| ITEM_LIST
|
||||
| EXTERN_ITEM_LIST
|
||||
| USE_TREE_LIST
|
||||
| BLOCK
|
||||
| BLOCK_EXPR
|
||||
| MATCH_ARM_LIST
|
||||
| ENUM_VARIANT_LIST
|
||||
| TOKEN_TREE => Some(FoldKind::Block),
|
||||
|
|
|
@ -844,4 +844,40 @@ fn func(foo: i32) { if true { <|>foo; }; }
|
|||
&["fn foo()\n```\n\n<- `\u{3000}` here"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hover_function_show_qualifiers() {
|
||||
check_hover_result(
|
||||
"
|
||||
//- /lib.rs
|
||||
async fn foo<|>() {}
|
||||
",
|
||||
&["async fn foo()"],
|
||||
);
|
||||
check_hover_result(
|
||||
"
|
||||
//- /lib.rs
|
||||
pub const unsafe fn foo<|>() {}
|
||||
",
|
||||
&["pub const unsafe fn foo()"],
|
||||
);
|
||||
check_hover_result(
|
||||
r#"
|
||||
//- /lib.rs
|
||||
pub(crate) async unsafe extern "C" fn foo<|>() {}
|
||||
"#,
|
||||
&[r#"pub(crate) async unsafe extern "C" fn foo()"#],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hover_trait_show_qualifiers() {
|
||||
check_hover_result(
|
||||
"
|
||||
//- /lib.rs
|
||||
unsafe trait foo<|>() {}
|
||||
",
|
||||
&["unsafe trait foo"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,8 +129,10 @@ fn has_comma_after(node: &SyntaxNode) -> bool {
|
|||
}
|
||||
|
||||
fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> {
|
||||
let block = ast::Block::cast(token.parent())?;
|
||||
let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?;
|
||||
let block_expr = ast::BlockExpr::cast(token.parent())?;
|
||||
if !block_expr.is_standalone() {
|
||||
return None;
|
||||
}
|
||||
let expr = extract_trivial_expression(&block_expr)?;
|
||||
|
||||
let block_range = block_expr.syntax().text_range();
|
||||
|
@ -662,4 +664,67 @@ fn main() {
|
|||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_lines_mandatory_blocks_block() {
|
||||
check_join_lines(
|
||||
r"
|
||||
<|>fn foo() {
|
||||
92
|
||||
}
|
||||
",
|
||||
r"
|
||||
<|>fn foo() { 92
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
check_join_lines(
|
||||
r"
|
||||
fn foo() {
|
||||
<|>if true {
|
||||
92
|
||||
}
|
||||
}
|
||||
",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>if true { 92
|
||||
}
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
check_join_lines(
|
||||
r"
|
||||
fn foo() {
|
||||
<|>loop {
|
||||
92
|
||||
}
|
||||
}
|
||||
",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>loop { 92
|
||||
}
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
check_join_lines(
|
||||
r"
|
||||
fn foo() {
|
||||
<|>unsafe {
|
||||
92
|
||||
}
|
||||
}
|
||||
",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>unsafe { 92
|
||||
}
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,9 +120,8 @@ SOURCE_FILE@0..11
|
|||
R_PAREN@7..8 ")"
|
||||
WHITESPACE@8..9 " "
|
||||
BLOCK_EXPR@9..11
|
||||
BLOCK@9..11
|
||||
L_CURLY@9..10 "{"
|
||||
R_CURLY@10..11 "}"
|
||||
L_CURLY@9..10 "{"
|
||||
R_CURLY@10..11 "}"
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
|
@ -153,26 +152,25 @@ SOURCE_FILE@0..60
|
|||
R_PAREN@8..9 ")"
|
||||
WHITESPACE@9..10 " "
|
||||
BLOCK_EXPR@10..60
|
||||
BLOCK@10..60
|
||||
L_CURLY@10..11 "{"
|
||||
WHITESPACE@11..16 "\n "
|
||||
EXPR_STMT@16..58
|
||||
MACRO_CALL@16..57
|
||||
PATH@16..22
|
||||
PATH_SEGMENT@16..22
|
||||
NAME_REF@16..22
|
||||
IDENT@16..22 "assert"
|
||||
BANG@22..23 "!"
|
||||
TOKEN_TREE@23..57
|
||||
L_PAREN@23..24 "("
|
||||
STRING@24..52 "\"\n fn foo() {\n ..."
|
||||
COMMA@52..53 ","
|
||||
WHITESPACE@53..54 " "
|
||||
STRING@54..56 "\"\""
|
||||
R_PAREN@56..57 ")"
|
||||
SEMICOLON@57..58 ";"
|
||||
WHITESPACE@58..59 "\n"
|
||||
R_CURLY@59..60 "}"
|
||||
L_CURLY@10..11 "{"
|
||||
WHITESPACE@11..16 "\n "
|
||||
EXPR_STMT@16..58
|
||||
MACRO_CALL@16..57
|
||||
PATH@16..22
|
||||
PATH_SEGMENT@16..22
|
||||
NAME_REF@16..22
|
||||
IDENT@16..22 "assert"
|
||||
BANG@22..23 "!"
|
||||
TOKEN_TREE@23..57
|
||||
L_PAREN@23..24 "("
|
||||
STRING@24..52 "\"\n fn foo() {\n ..."
|
||||
COMMA@52..53 ","
|
||||
WHITESPACE@53..54 " "
|
||||
STRING@54..56 "\"\""
|
||||
R_PAREN@56..57 ")"
|
||||
SEMICOLON@57..58 ";"
|
||||
WHITESPACE@58..59 "\n"
|
||||
R_CURLY@59..60 "}"
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
|
@ -196,9 +194,8 @@ FN_DEF@0..11
|
|||
R_PAREN@7..8 ")"
|
||||
WHITESPACE@8..9 " "
|
||||
BLOCK_EXPR@9..11
|
||||
BLOCK@9..11
|
||||
L_CURLY@9..10 "{"
|
||||
R_CURLY@10..11 "}"
|
||||
L_CURLY@9..10 "{"
|
||||
R_CURLY@10..11 "}"
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
|
@ -265,10 +262,9 @@ SOURCE_FILE@0..12
|
|||
R_PAREN@7..8 ")"
|
||||
WHITESPACE@8..9 " "
|
||||
BLOCK_EXPR@9..12
|
||||
BLOCK@9..12
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..11 "\n"
|
||||
R_CURLY@11..12 "}"
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..11 "\n"
|
||||
R_CURLY@11..12 "}"
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
|
@ -300,10 +296,9 @@ SOURCE_FILE@0..12
|
|||
R_PAREN@7..8 ")"
|
||||
WHITESPACE@8..9 " "
|
||||
BLOCK_EXPR@9..12
|
||||
BLOCK@9..12
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..11 "\n"
|
||||
R_CURLY@11..12 "}"
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..11 "\n"
|
||||
R_CURLY@11..12 "}"
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
|
@ -334,10 +329,9 @@ SOURCE_FILE@0..25
|
|||
R_PAREN@7..8 ")"
|
||||
WHITESPACE@8..9 " "
|
||||
BLOCK_EXPR@9..12
|
||||
BLOCK@9..12
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..11 "\n"
|
||||
R_CURLY@11..12 "}"
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..11 "\n"
|
||||
R_CURLY@11..12 "}"
|
||||
WHITESPACE@12..13 "\n"
|
||||
FN_DEF@13..25
|
||||
FN_KW@13..15 "fn"
|
||||
|
@ -349,10 +343,9 @@ SOURCE_FILE@0..25
|
|||
R_PAREN@20..21 ")"
|
||||
WHITESPACE@21..22 " "
|
||||
BLOCK_EXPR@22..25
|
||||
BLOCK@22..25
|
||||
L_CURLY@22..23 "{"
|
||||
WHITESPACE@23..24 "\n"
|
||||
R_CURLY@24..25 "}"
|
||||
L_CURLY@22..23 "{"
|
||||
WHITESPACE@23..24 "\n"
|
||||
R_CURLY@24..25 "}"
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
|
|
|
@ -266,21 +266,20 @@ fn test_expr_order() {
|
|||
L_PAREN@5..6 "("
|
||||
R_PAREN@6..7 ")"
|
||||
BLOCK_EXPR@7..15
|
||||
BLOCK@7..15
|
||||
L_CURLY@7..8 "{"
|
||||
EXPR_STMT@8..14
|
||||
BIN_EXPR@8..13
|
||||
BIN_EXPR@8..11
|
||||
LITERAL@8..9
|
||||
INT_NUMBER@8..9 "1"
|
||||
PLUS@9..10 "+"
|
||||
LITERAL@10..11
|
||||
INT_NUMBER@10..11 "1"
|
||||
STAR@11..12 "*"
|
||||
LITERAL@12..13
|
||||
INT_NUMBER@12..13 "2"
|
||||
SEMICOLON@13..14 ";"
|
||||
R_CURLY@14..15 "}""#,
|
||||
L_CURLY@7..8 "{"
|
||||
EXPR_STMT@8..14
|
||||
BIN_EXPR@8..13
|
||||
BIN_EXPR@8..11
|
||||
LITERAL@8..9
|
||||
INT_NUMBER@8..9 "1"
|
||||
PLUS@9..10 "+"
|
||||
LITERAL@10..11
|
||||
INT_NUMBER@10..11 "1"
|
||||
STAR@11..12 "*"
|
||||
LITERAL@12..13
|
||||
INT_NUMBER@12..13 "2"
|
||||
SEMICOLON@13..14 ";"
|
||||
R_CURLY@14..15 "}""#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1114,68 +1113,67 @@ fn test_vec() {
|
|||
assert_eq!(
|
||||
format!("{:#?}", tree).trim(),
|
||||
r#"BLOCK_EXPR@0..45
|
||||
BLOCK@0..45
|
||||
L_CURLY@0..1 "{"
|
||||
LET_STMT@1..20
|
||||
LET_KW@1..4 "let"
|
||||
BIND_PAT@4..8
|
||||
MUT_KW@4..7 "mut"
|
||||
NAME@7..8
|
||||
IDENT@7..8 "v"
|
||||
EQ@8..9 "="
|
||||
CALL_EXPR@9..19
|
||||
PATH_EXPR@9..17
|
||||
PATH@9..17
|
||||
PATH@9..12
|
||||
PATH_SEGMENT@9..12
|
||||
NAME_REF@9..12
|
||||
IDENT@9..12 "Vec"
|
||||
COLON2@12..14 "::"
|
||||
PATH_SEGMENT@14..17
|
||||
NAME_REF@14..17
|
||||
IDENT@14..17 "new"
|
||||
ARG_LIST@17..19
|
||||
L_PAREN@17..18 "("
|
||||
R_PAREN@18..19 ")"
|
||||
SEMICOLON@19..20 ";"
|
||||
EXPR_STMT@20..33
|
||||
METHOD_CALL_EXPR@20..32
|
||||
PATH_EXPR@20..21
|
||||
PATH@20..21
|
||||
PATH_SEGMENT@20..21
|
||||
NAME_REF@20..21
|
||||
IDENT@20..21 "v"
|
||||
DOT@21..22 "."
|
||||
NAME_REF@22..26
|
||||
IDENT@22..26 "push"
|
||||
ARG_LIST@26..32
|
||||
L_PAREN@26..27 "("
|
||||
LITERAL@27..31
|
||||
INT_NUMBER@27..31 "1u32"
|
||||
R_PAREN@31..32 ")"
|
||||
SEMICOLON@32..33 ";"
|
||||
EXPR_STMT@33..43
|
||||
METHOD_CALL_EXPR@33..42
|
||||
PATH_EXPR@33..34
|
||||
PATH@33..34
|
||||
PATH_SEGMENT@33..34
|
||||
NAME_REF@33..34
|
||||
IDENT@33..34 "v"
|
||||
DOT@34..35 "."
|
||||
NAME_REF@35..39
|
||||
IDENT@35..39 "push"
|
||||
ARG_LIST@39..42
|
||||
L_PAREN@39..40 "("
|
||||
LITERAL@40..41
|
||||
INT_NUMBER@40..41 "2"
|
||||
R_PAREN@41..42 ")"
|
||||
SEMICOLON@42..43 ";"
|
||||
PATH_EXPR@43..44
|
||||
PATH@43..44
|
||||
PATH_SEGMENT@43..44
|
||||
NAME_REF@43..44
|
||||
IDENT@43..44 "v"
|
||||
R_CURLY@44..45 "}""#
|
||||
L_CURLY@0..1 "{"
|
||||
LET_STMT@1..20
|
||||
LET_KW@1..4 "let"
|
||||
BIND_PAT@4..8
|
||||
MUT_KW@4..7 "mut"
|
||||
NAME@7..8
|
||||
IDENT@7..8 "v"
|
||||
EQ@8..9 "="
|
||||
CALL_EXPR@9..19
|
||||
PATH_EXPR@9..17
|
||||
PATH@9..17
|
||||
PATH@9..12
|
||||
PATH_SEGMENT@9..12
|
||||
NAME_REF@9..12
|
||||
IDENT@9..12 "Vec"
|
||||
COLON2@12..14 "::"
|
||||
PATH_SEGMENT@14..17
|
||||
NAME_REF@14..17
|
||||
IDENT@14..17 "new"
|
||||
ARG_LIST@17..19
|
||||
L_PAREN@17..18 "("
|
||||
R_PAREN@18..19 ")"
|
||||
SEMICOLON@19..20 ";"
|
||||
EXPR_STMT@20..33
|
||||
METHOD_CALL_EXPR@20..32
|
||||
PATH_EXPR@20..21
|
||||
PATH@20..21
|
||||
PATH_SEGMENT@20..21
|
||||
NAME_REF@20..21
|
||||
IDENT@20..21 "v"
|
||||
DOT@21..22 "."
|
||||
NAME_REF@22..26
|
||||
IDENT@22..26 "push"
|
||||
ARG_LIST@26..32
|
||||
L_PAREN@26..27 "("
|
||||
LITERAL@27..31
|
||||
INT_NUMBER@27..31 "1u32"
|
||||
R_PAREN@31..32 ")"
|
||||
SEMICOLON@32..33 ";"
|
||||
EXPR_STMT@33..43
|
||||
METHOD_CALL_EXPR@33..42
|
||||
PATH_EXPR@33..34
|
||||
PATH@33..34
|
||||
PATH_SEGMENT@33..34
|
||||
NAME_REF@33..34
|
||||
IDENT@33..34 "v"
|
||||
DOT@34..35 "."
|
||||
NAME_REF@35..39
|
||||
IDENT@35..39 "push"
|
||||
ARG_LIST@39..42
|
||||
L_PAREN@39..40 "("
|
||||
LITERAL@40..41
|
||||
INT_NUMBER@40..41 "2"
|
||||
R_PAREN@41..42 ")"
|
||||
SEMICOLON@42..43 ";"
|
||||
PATH_EXPR@43..44
|
||||
PATH@43..44
|
||||
PATH_SEGMENT@43..44
|
||||
NAME_REF@43..44
|
||||
IDENT@43..44 "v"
|
||||
R_CURLY@44..45 "}""#
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ pub(crate) mod fragments {
|
|||
use super::*;
|
||||
|
||||
pub(crate) use super::{
|
||||
expressions::block, paths::type_path as path, patterns::pattern, types::type_,
|
||||
expressions::block_expr, paths::type_path as path, patterns::pattern, types::type_,
|
||||
};
|
||||
|
||||
pub(crate) fn expr(p: &mut Parser) {
|
||||
|
@ -143,7 +143,7 @@ pub(crate) fn reparser(
|
|||
parent: Option<SyntaxKind>,
|
||||
) -> Option<fn(&mut Parser)> {
|
||||
let res = match node {
|
||||
BLOCK => expressions::naked_block,
|
||||
BLOCK_EXPR => expressions::block_expr,
|
||||
RECORD_FIELD_DEF_LIST => items::record_field_def_list,
|
||||
RECORD_FIELD_LIST => items::record_field_list,
|
||||
ENUM_VARIANT_LIST => items::enum_variant_list,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
mod atom;
|
||||
|
||||
pub(crate) use self::atom::match_arm_list;
|
||||
pub(crate) use self::atom::{block_expr, match_arm_list};
|
||||
pub(super) use self::atom::{literal, LITERAL_FIRST};
|
||||
use super::*;
|
||||
|
||||
|
@ -49,28 +49,6 @@ fn expr_no_struct(p: &mut Parser) {
|
|||
expr_bp(p, r, 1);
|
||||
}
|
||||
|
||||
// test block
|
||||
// fn a() {}
|
||||
// fn b() { let _ = 1; }
|
||||
// fn c() { 1; 2; }
|
||||
// fn d() { 1; 2 }
|
||||
pub(crate) fn block(p: &mut Parser) {
|
||||
if !p.at(T!['{']) {
|
||||
p.error("expected a block");
|
||||
return;
|
||||
}
|
||||
atom::block_expr(p, None);
|
||||
}
|
||||
|
||||
pub(crate) fn naked_block(p: &mut Parser) {
|
||||
assert!(p.at(T!['{']));
|
||||
let m = p.start();
|
||||
p.bump(T!['{']);
|
||||
expr_block_contents(p);
|
||||
p.expect(T!['}']);
|
||||
m.complete(p, BLOCK);
|
||||
}
|
||||
|
||||
fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool {
|
||||
match kind {
|
||||
BIN_EXPR | RANGE_EXPR | IF_EXPR => false,
|
||||
|
@ -197,7 +175,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn expr_block_contents(p: &mut Parser) {
|
||||
pub(super) fn expr_block_contents(p: &mut Parser) {
|
||||
// This is checked by a validator
|
||||
attributes::inner_attributes(p);
|
||||
|
||||
|
|
|
@ -92,7 +92,12 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
|
|||
T![loop] => loop_expr(p, Some(m)),
|
||||
T![for] => for_expr(p, Some(m)),
|
||||
T![while] => while_expr(p, Some(m)),
|
||||
T!['{'] => block_expr(p, Some(m)),
|
||||
// test labeled_block
|
||||
// fn f() { 'label: {}; }
|
||||
T!['{'] => {
|
||||
block_expr(p);
|
||||
m.complete(p, EFFECT_EXPR)
|
||||
}
|
||||
_ => {
|
||||
// test_err misplaced_label_err
|
||||
// fn main() {
|
||||
|
@ -108,13 +113,17 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
|
|||
let m = p.start();
|
||||
p.bump(T![async]);
|
||||
p.eat(T![move]);
|
||||
block_expr(p, Some(m))
|
||||
block_expr(p);
|
||||
m.complete(p, EFFECT_EXPR)
|
||||
}
|
||||
T![match] => match_expr(p),
|
||||
// test unsafe_block
|
||||
// fn f() { unsafe { } }
|
||||
T![unsafe] if la == T!['{'] => {
|
||||
let m = p.start();
|
||||
p.bump(T![unsafe]);
|
||||
block_expr(p, Some(m))
|
||||
block_expr(p);
|
||||
m.complete(p, EFFECT_EXPR)
|
||||
}
|
||||
T!['{'] => {
|
||||
// test for_range_from
|
||||
|
@ -123,7 +132,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
|
|||
// break;
|
||||
// }
|
||||
// }
|
||||
block_expr(p, None)
|
||||
block_expr_unchecked(p)
|
||||
}
|
||||
T![return] => return_expr(p),
|
||||
T![continue] => continue_expr(p),
|
||||
|
@ -134,7 +143,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
|
|||
}
|
||||
};
|
||||
let blocklike = match done.kind() {
|
||||
IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR | TRY_BLOCK_EXPR => {
|
||||
IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR | EFFECT_EXPR => {
|
||||
BlockLike::Block
|
||||
}
|
||||
_ => BlockLike::NotBlock,
|
||||
|
@ -231,13 +240,9 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker {
|
|||
p.eat(T![move]);
|
||||
params::param_list_closure(p);
|
||||
if opt_fn_ret_type(p) {
|
||||
if p.at(T!['{']) {
|
||||
// test lambda_ret_block
|
||||
// fn main() { || -> i32 { 92 }(); }
|
||||
block_expr(p, None);
|
||||
} else {
|
||||
p.error("expected `{`");
|
||||
}
|
||||
// test lambda_ret_block
|
||||
// fn main() { || -> i32 { 92 }(); }
|
||||
block_expr(p);
|
||||
} else {
|
||||
if p.at_ts(EXPR_FIRST) {
|
||||
expr(p);
|
||||
|
@ -261,13 +266,13 @@ fn if_expr(p: &mut Parser) -> CompletedMarker {
|
|||
let m = p.start();
|
||||
p.bump(T![if]);
|
||||
cond(p);
|
||||
block(p);
|
||||
block_expr(p);
|
||||
if p.at(T![else]) {
|
||||
p.bump(T![else]);
|
||||
if p.at(T![if]) {
|
||||
if_expr(p);
|
||||
} else {
|
||||
block(p);
|
||||
block_expr(p);
|
||||
}
|
||||
}
|
||||
m.complete(p, IF_EXPR)
|
||||
|
@ -295,7 +300,7 @@ fn loop_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
|
|||
assert!(p.at(T![loop]));
|
||||
let m = m.unwrap_or_else(|| p.start());
|
||||
p.bump(T![loop]);
|
||||
block(p);
|
||||
block_expr(p);
|
||||
m.complete(p, LOOP_EXPR)
|
||||
}
|
||||
|
||||
|
@ -310,7 +315,7 @@ fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
|
|||
let m = m.unwrap_or_else(|| p.start());
|
||||
p.bump(T![while]);
|
||||
cond(p);
|
||||
block(p);
|
||||
block_expr(p);
|
||||
m.complete(p, WHILE_EXPR)
|
||||
}
|
||||
|
||||
|
@ -325,7 +330,7 @@ fn for_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
|
|||
patterns::pattern(p);
|
||||
p.expect(T![in]);
|
||||
expr_no_struct(p);
|
||||
block(p);
|
||||
block_expr(p);
|
||||
m.complete(p, FOR_EXPR)
|
||||
}
|
||||
|
||||
|
@ -458,16 +463,25 @@ fn match_guard(p: &mut Parser) -> CompletedMarker {
|
|||
m.complete(p, MATCH_GUARD)
|
||||
}
|
||||
|
||||
// test block_expr
|
||||
// fn foo() {
|
||||
// {};
|
||||
// unsafe {};
|
||||
// 'label: {};
|
||||
// }
|
||||
pub(super) fn block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
|
||||
// test block
|
||||
// fn a() {}
|
||||
// fn b() { let _ = 1; }
|
||||
// fn c() { 1; 2; }
|
||||
// fn d() { 1; 2 }
|
||||
pub(crate) fn block_expr(p: &mut Parser) {
|
||||
if !p.at(T!['{']) {
|
||||
p.error("expected a block");
|
||||
return;
|
||||
}
|
||||
block_expr_unchecked(p);
|
||||
}
|
||||
|
||||
fn block_expr_unchecked(p: &mut Parser) -> CompletedMarker {
|
||||
assert!(p.at(T!['{']));
|
||||
let m = m.unwrap_or_else(|| p.start());
|
||||
naked_block(p);
|
||||
let m = p.start();
|
||||
p.bump(T!['{']);
|
||||
expr_block_contents(p);
|
||||
p.expect(T!['}']);
|
||||
m.complete(p, BLOCK_EXPR)
|
||||
}
|
||||
|
||||
|
@ -535,9 +549,25 @@ fn break_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker {
|
|||
fn try_block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
|
||||
assert!(p.at(T![try]));
|
||||
let m = m.unwrap_or_else(|| p.start());
|
||||
// Special-case `try!` as macro.
|
||||
// This is a hack until we do proper edition support
|
||||
if p.nth_at(1, T![!]) {
|
||||
// test try_macro_fallback
|
||||
// fn foo() { try!(Ok(())); }
|
||||
let path = p.start();
|
||||
let path_segment = p.start();
|
||||
let name_ref = p.start();
|
||||
p.bump_remap(IDENT);
|
||||
name_ref.complete(p, NAME_REF);
|
||||
path_segment.complete(p, PATH_SEGMENT);
|
||||
path.complete(p, PATH);
|
||||
let _block_like = items::macro_call_after_excl(p);
|
||||
return m.complete(p, MACRO_CALL);
|
||||
}
|
||||
|
||||
p.bump(T![try]);
|
||||
block(p);
|
||||
m.complete(p, TRY_EXPR)
|
||||
block_expr(p);
|
||||
m.complete(p, EFFECT_EXPR)
|
||||
}
|
||||
|
||||
// test box_expr
|
||||
|
|
|
@ -329,7 +329,7 @@ fn fn_def(p: &mut Parser) {
|
|||
if p.at(T![;]) {
|
||||
p.bump(T![;]);
|
||||
} else {
|
||||
expressions::block(p)
|
||||
expressions::block_expr(p)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,6 +415,17 @@ pub(super) fn macro_call_after_excl(p: &mut Parser) -> BlockLike {
|
|||
if p.at(IDENT) {
|
||||
name(p);
|
||||
}
|
||||
// Special-case `macro_rules! try`.
|
||||
// This is a hack until we do proper edition support
|
||||
|
||||
// test try_macro_rules
|
||||
// macro_rules! try { () => {} }
|
||||
if p.at(T![try]) {
|
||||
let m = p.start();
|
||||
p.bump_remap(IDENT);
|
||||
m.complete(p, NAME);
|
||||
}
|
||||
|
||||
match p.current() {
|
||||
T!['{'] => {
|
||||
token_tree(p);
|
||||
|
|
|
@ -47,7 +47,7 @@ fn use_tree(p: &mut Parser, top_level: bool) {
|
|||
// use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
|
||||
// use {path::from::root}; // Rust 2015
|
||||
// use ::{some::arbritrary::path}; // Rust 2015
|
||||
// use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig
|
||||
// use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting
|
||||
T!['{'] => {
|
||||
use_tree_list(p);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ fn type_arg(p: &mut Parser) {
|
|||
m.complete(p, ASSOC_TYPE_ARG);
|
||||
}
|
||||
T!['{'] => {
|
||||
expressions::block(p);
|
||||
expressions::block_expr(p);
|
||||
m.complete(p, CONST_ARG);
|
||||
}
|
||||
k if k.is_literal() => {
|
||||
|
|
|
@ -112,7 +112,7 @@ pub fn parse_fragment(
|
|||
FragmentKind::Type => grammar::fragments::type_,
|
||||
FragmentKind::Pattern => grammar::fragments::pattern,
|
||||
FragmentKind::Item => grammar::fragments::item,
|
||||
FragmentKind::Block => grammar::fragments::block,
|
||||
FragmentKind::Block => grammar::fragments::block_expr,
|
||||
FragmentKind::Visibility => grammar::fragments::opt_visibility,
|
||||
FragmentKind::MetaItem => grammar::fragments::meta_item,
|
||||
FragmentKind::Statement => grammar::fragments::stmt,
|
||||
|
|
|
@ -191,7 +191,7 @@ pub enum SyntaxKind {
|
|||
RECORD_LIT,
|
||||
RECORD_FIELD_LIST,
|
||||
RECORD_FIELD,
|
||||
TRY_BLOCK_EXPR,
|
||||
EFFECT_EXPR,
|
||||
BOX_EXPR,
|
||||
CALL_EXPR,
|
||||
INDEX_EXPR,
|
||||
|
@ -204,7 +204,6 @@ pub enum SyntaxKind {
|
|||
PREFIX_EXPR,
|
||||
RANGE_EXPR,
|
||||
BIN_EXPR,
|
||||
BLOCK,
|
||||
EXTERN_BLOCK,
|
||||
EXTERN_ITEM_LIST,
|
||||
ENUM_VARIANT,
|
||||
|
|
|
@ -21,4 +21,4 @@ test_utils = { path = "../test_utils" }
|
|||
cargo_metadata = "0.9.1"
|
||||
difference = "2.0.0"
|
||||
# used as proc macro test target
|
||||
serde_derive = "=1.0.104"
|
||||
serde_derive = "=1.0.106"
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
//! Driver for proc macro server
|
||||
|
||||
use crate::{expand_task, list_macros};
|
||||
use crate::ProcMacroSrv;
|
||||
use ra_proc_macro::msg::{self, Message};
|
||||
use std::io;
|
||||
|
||||
pub fn run() -> io::Result<()> {
|
||||
let mut srv = ProcMacroSrv::default();
|
||||
|
||||
while let Some(req) = read_request()? {
|
||||
let res = match req {
|
||||
msg::Request::ListMacro(task) => Ok(msg::Response::ListMacro(list_macros(&task))),
|
||||
msg::Request::ListMacro(task) => srv.list_macros(&task).map(msg::Response::ListMacro),
|
||||
msg::Request::ExpansionMacro(task) => {
|
||||
expand_task(&task).map(msg::Response::ExpansionMacro)
|
||||
srv.expand(&task).map(msg::Response::ExpansionMacro)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,13 +2,12 @@
|
|||
|
||||
use crate::{proc_macro::bridge, rustc_server::TokenStream};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use goblin::{mach::Mach, Object};
|
||||
use libloading::Library;
|
||||
use memmap::Mmap;
|
||||
use ra_proc_macro::ProcMacroKind;
|
||||
|
||||
use std::io;
|
||||
|
||||
const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
|
||||
|
@ -109,23 +108,21 @@ impl ProcMacroLibraryLibloading {
|
|||
}
|
||||
}
|
||||
|
||||
type ProcMacroLibraryImpl = ProcMacroLibraryLibloading;
|
||||
|
||||
pub struct Expander {
|
||||
libs: Vec<ProcMacroLibraryImpl>,
|
||||
inner: ProcMacroLibraryLibloading,
|
||||
}
|
||||
|
||||
impl Expander {
|
||||
pub fn new(lib: &Path) -> Result<Expander, String> {
|
||||
pub fn new(lib: &Path) -> io::Result<Expander> {
|
||||
// Some libraries for dynamic loading require canonicalized path even when it is
|
||||
// already absolute
|
||||
let lib = lib
|
||||
.canonicalize()
|
||||
.unwrap_or_else(|err| panic!("Cannot canonicalize {}: {:?}", lib.display(), err));
|
||||
let lib = lib.canonicalize()?;
|
||||
|
||||
let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;
|
||||
let lib = ensure_file_with_lock_free_access(&lib)?;
|
||||
|
||||
Ok(Expander { libs: vec![library] })
|
||||
let library = ProcMacroLibraryLibloading::open(&lib)?;
|
||||
|
||||
Ok(Expander { inner: library })
|
||||
}
|
||||
|
||||
pub fn expand(
|
||||
|
@ -141,38 +138,36 @@ impl Expander {
|
|||
TokenStream::with_subtree(attr.clone())
|
||||
});
|
||||
|
||||
for lib in &self.libs {
|
||||
for proc_macro in &lib.exported_macros {
|
||||
match proc_macro {
|
||||
bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
|
||||
if *trait_name == macro_name =>
|
||||
{
|
||||
let res = client.run(
|
||||
&crate::proc_macro::bridge::server::SameThread,
|
||||
crate::rustc_server::Rustc::default(),
|
||||
parsed_body,
|
||||
);
|
||||
return res.map(|it| it.subtree);
|
||||
}
|
||||
bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
|
||||
let res = client.run(
|
||||
&crate::proc_macro::bridge::server::SameThread,
|
||||
crate::rustc_server::Rustc::default(),
|
||||
parsed_body,
|
||||
);
|
||||
return res.map(|it| it.subtree);
|
||||
}
|
||||
bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
|
||||
let res = client.run(
|
||||
&crate::proc_macro::bridge::server::SameThread,
|
||||
crate::rustc_server::Rustc::default(),
|
||||
parsed_attributes,
|
||||
parsed_body,
|
||||
);
|
||||
return res.map(|it| it.subtree);
|
||||
}
|
||||
_ => continue,
|
||||
for proc_macro in &self.inner.exported_macros {
|
||||
match proc_macro {
|
||||
bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
|
||||
if *trait_name == macro_name =>
|
||||
{
|
||||
let res = client.run(
|
||||
&crate::proc_macro::bridge::server::SameThread,
|
||||
crate::rustc_server::Rustc::default(),
|
||||
parsed_body,
|
||||
);
|
||||
return res.map(|it| it.subtree);
|
||||
}
|
||||
bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
|
||||
let res = client.run(
|
||||
&crate::proc_macro::bridge::server::SameThread,
|
||||
crate::rustc_server::Rustc::default(),
|
||||
parsed_body,
|
||||
);
|
||||
return res.map(|it| it.subtree);
|
||||
}
|
||||
bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
|
||||
let res = client.run(
|
||||
&crate::proc_macro::bridge::server::SameThread,
|
||||
crate::rustc_server::Rustc::default(),
|
||||
parsed_attributes,
|
||||
parsed_body,
|
||||
);
|
||||
return res.map(|it| it.subtree);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,9 +175,9 @@ impl Expander {
|
|||
}
|
||||
|
||||
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
|
||||
self.libs
|
||||
self.inner
|
||||
.exported_macros
|
||||
.iter()
|
||||
.flat_map(|it| &it.exported_macros)
|
||||
.map(|proc_macro| match proc_macro {
|
||||
bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
|
||||
(trait_name.to_string(), ProcMacroKind::CustomDerive)
|
||||
|
@ -197,3 +192,33 @@ impl Expander {
|
|||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy the dylib to temp directory to prevent locking in Windows
|
||||
#[cfg(windows)]
|
||||
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
|
||||
use std::{ffi::OsString, time::SystemTime};
|
||||
|
||||
let mut to = std::env::temp_dir();
|
||||
|
||||
let file_name = path.file_name().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!("File path is invalid: {}", path.display()),
|
||||
)
|
||||
})?;
|
||||
|
||||
// generate a time deps unique number
|
||||
let t = SystemTime::now().duration_since(std::time::UNIX_EPOCH).expect("Time went backwards");
|
||||
|
||||
let mut unique_name = OsString::from(t.as_millis().to_string());
|
||||
unique_name.push(file_name);
|
||||
|
||||
to.push(unique_name);
|
||||
std::fs::copy(path, &to).unwrap();
|
||||
Ok(to)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
|
||||
Ok(path.to_path_buf())
|
||||
}
|
||||
|
|
|
@ -21,28 +21,46 @@ mod dylib;
|
|||
|
||||
use proc_macro::bridge::client::TokenStream;
|
||||
use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
|
||||
let expander = create_expander(&task.lib);
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ProcMacroSrv {
|
||||
expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>,
|
||||
}
|
||||
|
||||
match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
|
||||
Ok(expansion) => Ok(ExpansionResult { expansion }),
|
||||
Err(msg) => {
|
||||
Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg))
|
||||
impl ProcMacroSrv {
|
||||
pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> {
|
||||
let expander = self.expander(&task.lib)?;
|
||||
match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
|
||||
Ok(expansion) => Ok(ExpansionResult { expansion }),
|
||||
Err(msg) => {
|
||||
Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult {
|
||||
let expander = create_expander(&task.lib);
|
||||
pub fn list_macros(&mut self, task: &ListMacrosTask) -> Result<ListMacrosResult, String> {
|
||||
let expander = self.expander(&task.lib)?;
|
||||
Ok(ListMacrosResult { macros: expander.list_macros() })
|
||||
}
|
||||
|
||||
ListMacrosResult { macros: expander.list_macros() }
|
||||
}
|
||||
fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> {
|
||||
let time = fs::metadata(path).and_then(|it| it.modified()).map_err(|err| {
|
||||
format!("Failed to get file metadata for {}: {:?}", path.display(), err)
|
||||
})?;
|
||||
|
||||
fn create_expander(lib: &Path) -> dylib::Expander {
|
||||
dylib::Expander::new(lib)
|
||||
.unwrap_or_else(|err| panic!("Cannot create expander for {}: {:?}", lib.display(), err))
|
||||
Ok(match self.expanders.entry((path.to_path_buf(), time)) {
|
||||
Entry::Vacant(v) => v.insert(dylib::Expander::new(path).map_err(|err| {
|
||||
format!("Cannot create expander for {}: {:?}", path.display(), err)
|
||||
})?),
|
||||
Entry::Occupied(e) => e.into_mut(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub mod cli;
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
SUBTREE $
|
||||
PUNCH # [alone] 4294967295
|
||||
SUBTREE [] 4294967295
|
||||
IDENT doc 4294967295
|
||||
SUBTREE () 4294967295
|
||||
IDENT hidden 4294967295
|
||||
PUNCH # [alone] 4294967295
|
||||
SUBTREE [] 4294967295
|
||||
IDENT allow 4294967295
|
||||
SUBTREE () 4294967295
|
||||
|
@ -184,4 +189,4 @@ SUBTREE $
|
|||
IDENT end 4294967295
|
||||
SUBTREE () 4294967295
|
||||
IDENT __serde_state 4294967295
|
||||
PUNCH ; [alone] 4294967295
|
||||
PUNCH ; [alone] 4294967295
|
||||
|
|
|
@ -10,7 +10,7 @@ fn test_derive_serialize_proc_macro() {
|
|||
assert_expand(
|
||||
"serde_derive",
|
||||
"Serialize",
|
||||
"1.0.104",
|
||||
"1.0.106",
|
||||
r##"struct Foo {}"##,
|
||||
include_str!("fixtures/test_serialize_proc_macro.txt"),
|
||||
);
|
||||
|
@ -21,7 +21,7 @@ fn test_derive_serialize_proc_macro_failed() {
|
|||
assert_expand(
|
||||
"serde_derive",
|
||||
"Serialize",
|
||||
"1.0.104",
|
||||
"1.0.106",
|
||||
r##"
|
||||
struct {}
|
||||
"##,
|
||||
|
@ -37,7 +37,7 @@ SUBTREE $
|
|||
|
||||
#[test]
|
||||
fn test_derive_proc_macro_list() {
|
||||
let res = list("serde_derive", "1.0.104").join("\n");
|
||||
let res = list("serde_derive", "1.0.106").join("\n");
|
||||
|
||||
assert_eq_text!(
|
||||
&res,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! utils used in proc-macro tests
|
||||
|
||||
use crate::dylib;
|
||||
use crate::list_macros;
|
||||
use crate::ProcMacroSrv;
|
||||
pub use difference::Changeset as __Changeset;
|
||||
use ra_proc_macro::ListMacrosTask;
|
||||
use std::str::FromStr;
|
||||
|
@ -59,7 +59,7 @@ pub fn assert_expand(
|
|||
pub fn list(crate_name: &str, version: &str) -> Vec<String> {
|
||||
let path = fixtures::dylib_path(crate_name, version);
|
||||
let task = ListMacrosTask { lib: path };
|
||||
|
||||
let res = list_macros(&task);
|
||||
let mut srv = ProcMacroSrv::default();
|
||||
let res = srv.list_macros(&task).unwrap();
|
||||
res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
|
||||
}
|
||||
|
|
|
@ -30,8 +30,9 @@ pub fn init_from(spec: &str) {
|
|||
pub type Label = &'static str;
|
||||
|
||||
/// This function starts a profiling scope in the current execution stack with a given description.
|
||||
/// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop.
|
||||
/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output.
|
||||
/// It returns a `Profile` struct that measures elapsed time between this method invocation and `Profile` struct drop.
|
||||
/// It supports nested profiling scopes in case when this function is invoked multiple times at the execution stack.
|
||||
/// In this case the profiling information will be nested at the output.
|
||||
/// Profiling information is being printed in the stderr.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -58,36 +59,35 @@ pub type Label = &'static str;
|
|||
/// ```
|
||||
pub fn profile(label: Label) -> Profiler {
|
||||
assert!(!label.is_empty());
|
||||
let enabled = PROFILING_ENABLED.load(Ordering::Relaxed)
|
||||
&& PROFILE_STACK.with(|stack| stack.borrow_mut().push(label));
|
||||
let label = if enabled { Some(label) } else { None };
|
||||
Profiler { label, detail: None }
|
||||
|
||||
if PROFILING_ENABLED.load(Ordering::Relaxed)
|
||||
&& PROFILE_STACK.with(|stack| stack.borrow_mut().push(label))
|
||||
{
|
||||
Profiler(Some(ProfilerImpl { label, detail: None }))
|
||||
} else {
|
||||
Profiler(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Profiler {
|
||||
label: Option<Label>,
|
||||
pub struct Profiler(Option<ProfilerImpl>);
|
||||
|
||||
struct ProfilerImpl {
|
||||
label: Label,
|
||||
detail: Option<String>,
|
||||
}
|
||||
|
||||
impl Profiler {
|
||||
pub fn detail(mut self, detail: impl FnOnce() -> String) -> Profiler {
|
||||
if self.label.is_some() {
|
||||
self.detail = Some(detail())
|
||||
if let Some(profiler) = &mut self.0 {
|
||||
profiler.detail = Some(detail())
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Profiler {
|
||||
impl Drop for ProfilerImpl {
|
||||
fn drop(&mut self) {
|
||||
match self {
|
||||
Profiler { label: Some(label), detail } => {
|
||||
PROFILE_STACK.with(|stack| {
|
||||
stack.borrow_mut().pop(label, detail.take());
|
||||
});
|
||||
}
|
||||
Profiler { label: None, .. } => (),
|
||||
}
|
||||
PROFILE_STACK.with(|it| it.borrow_mut().pop(self.label, self.detail.take()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,21 +179,18 @@ impl ProfileStack {
|
|||
pub fn pop(&mut self, label: Label, detail: Option<String>) {
|
||||
let start = self.starts.pop().unwrap();
|
||||
let duration = start.elapsed();
|
||||
let level = self.starts.len();
|
||||
self.messages.finish(Message { duration, label, detail });
|
||||
if level == 0 {
|
||||
if self.starts.is_empty() {
|
||||
let longer_than = self.filter.longer_than;
|
||||
// Convert to millis for comparison to avoid problems with rounding
|
||||
// (otherwise we could print `0ms` despite user's `>0` filter when
|
||||
// `duration` is just a few nanos).
|
||||
if duration.as_millis() > longer_than.as_millis() {
|
||||
let stderr = stderr();
|
||||
if let Some(root) = self.messages.root() {
|
||||
print(&self.messages, root, 0, longer_than, &mut stderr.lock());
|
||||
print(&self.messages, root, 0, longer_than, &mut stderr().lock());
|
||||
}
|
||||
}
|
||||
self.messages.clear();
|
||||
assert!(self.starts.is_empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ ra_db = { path = "../ra_db" }
|
|||
ra_cfg = { path = "../ra_cfg" }
|
||||
ra_proc_macro = { path = "../ra_proc_macro" }
|
||||
|
||||
serde = { version = "1.0.104", features = ["derive"] }
|
||||
serde = { version = "1.0.106", features = ["derive"] }
|
||||
serde_json = "1.0.48"
|
||||
|
||||
anyhow = "1.0.26"
|
||||
|
|
|
@ -13,7 +13,7 @@ doctest = false
|
|||
[dependencies]
|
||||
itertools = "0.9.0"
|
||||
rowan = "0.10.0"
|
||||
rustc_lexer = { version = "652.0.0", package = "rustc-ap-rustc_lexer" }
|
||||
rustc_lexer = { version = "656.0.0", package = "rustc-ap-rustc_lexer" }
|
||||
rustc-hash = "1.1.0"
|
||||
arrayvec = "0.5.1"
|
||||
once_cell = "1.3.1"
|
||||
|
@ -27,7 +27,7 @@ ra_parser = { path = "../ra_parser" }
|
|||
# ideally, `serde` should be enabled by `rust-analyzer`, but we enable it here
|
||||
# to reduce number of compilations
|
||||
smol_str = { version = "0.1.15", features = ["serde"] }
|
||||
serde = { version = "1.0.104", features = ["derive"] }
|
||||
serde = { version = "1.0.106", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
test_utils = { path = "../test_utils" }
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
};
|
||||
|
||||
pub use self::{
|
||||
expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
|
||||
expr_extensions::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp},
|
||||
extensions::{
|
||||
AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents,
|
||||
StructKind, TypeBoundKind, VisibilityKind,
|
||||
|
@ -242,6 +242,21 @@ fn test_comments_preserve_trailing_whitespace() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_four_slash_line_comment() {
|
||||
let file = SourceFile::parse(
|
||||
r#"
|
||||
//// too many slashes to be a doc comment
|
||||
/// doc comment
|
||||
mod foo {}
|
||||
"#,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
|
||||
assert_eq!("doc comment", module.doc_comment_text().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_where_predicates() {
|
||||
fn assert_bound(text: &str, bound: Option<TypeBound>) {
|
||||
|
|
|
@ -28,7 +28,7 @@ impl ast::BinExpr {
|
|||
|
||||
impl ast::FnDef {
|
||||
#[must_use]
|
||||
pub fn with_body(&self, body: ast::Block) -> ast::FnDef {
|
||||
pub fn with_body(&self, body: ast::BlockExpr) -> ast::FnDef {
|
||||
let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
|
||||
let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() {
|
||||
old_body.syntax().clone().into()
|
||||
|
|
|
@ -16,7 +16,7 @@ impl ast::Expr {
|
|||
| ast::Expr::WhileExpr(_)
|
||||
| ast::Expr::BlockExpr(_)
|
||||
| ast::Expr::MatchExpr(_)
|
||||
| ast::Expr::TryBlockExpr(_) => true,
|
||||
| ast::Expr::EffectExpr(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ impl ast::IfExpr {
|
|||
Some(res)
|
||||
}
|
||||
|
||||
fn blocks(&self) -> AstChildren<ast::BlockExpr> {
|
||||
pub fn blocks(&self) -> AstChildren<ast::BlockExpr> {
|
||||
support::children(self.syntax())
|
||||
}
|
||||
}
|
||||
|
@ -359,6 +359,33 @@ impl ast::Literal {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Effect {
|
||||
Async(SyntaxToken),
|
||||
Unsafe(SyntaxToken),
|
||||
Try(SyntaxToken),
|
||||
// Very much not an effect, but we stuff it into this node anyway
|
||||
Label(ast::Label),
|
||||
}
|
||||
|
||||
impl ast::EffectExpr {
|
||||
pub fn effect(&self) -> Effect {
|
||||
if let Some(token) = self.async_token() {
|
||||
return Effect::Async(token);
|
||||
}
|
||||
if let Some(token) = self.unsafe_token() {
|
||||
return Effect::Unsafe(token);
|
||||
}
|
||||
if let Some(token) = self.try_token() {
|
||||
return Effect::Try(token);
|
||||
}
|
||||
if let Some(label) = self.label() {
|
||||
return Effect::Label(label);
|
||||
}
|
||||
unreachable!("ast::EffectExpr without Effect")
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::BlockExpr {
|
||||
/// false if the block is an intrinsic part of the syntax and can't be
|
||||
/// replaced with arbitrary expression.
|
||||
|
@ -368,12 +395,12 @@ impl ast::BlockExpr {
|
|||
/// const FOO: () = { stand_alone };
|
||||
/// ```
|
||||
pub fn is_standalone(&self) -> bool {
|
||||
let kind = match self.syntax().parent() {
|
||||
let parent = match self.syntax().parent() {
|
||||
Some(it) => it,
|
||||
None => return true,
|
||||
Some(it) => it.kind(),
|
||||
};
|
||||
match kind {
|
||||
FN_DEF | MATCH_ARM | IF_EXPR | WHILE_EXPR | LOOP_EXPR | TRY_BLOCK_EXPR => false,
|
||||
match parent.kind() {
|
||||
FN_DEF | IF_EXPR | WHILE_EXPR | LOOP_EXPR | EFFECT_EXPR => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -407,7 +407,7 @@ impl ast::Visibility {
|
|||
} else if self.super_token().is_some() {
|
||||
VisibilityKind::PubSuper
|
||||
} else if self.self_token().is_some() {
|
||||
VisibilityKind::PubSuper
|
||||
VisibilityKind::PubSelf
|
||||
} else {
|
||||
VisibilityKind::Pub
|
||||
}
|
||||
|
@ -423,6 +423,10 @@ impl ast::MacroCall {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_bang(&self) -> bool {
|
||||
self.is_macro_rules().is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::LifetimeParam {
|
||||
|
|
|
@ -476,13 +476,16 @@ impl LoopExpr {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TryBlockExpr {
|
||||
pub struct EffectExpr {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl ast::AttrsOwner for TryBlockExpr {}
|
||||
impl TryBlockExpr {
|
||||
impl ast::AttrsOwner for EffectExpr {}
|
||||
impl EffectExpr {
|
||||
pub fn label(&self) -> Option<Label> { support::child(&self.syntax) }
|
||||
pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) }
|
||||
pub fn body(&self) -> Option<BlockExpr> { support::child(&self.syntax) }
|
||||
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
|
||||
pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) }
|
||||
pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -551,10 +554,12 @@ pub struct BlockExpr {
|
|||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl ast::AttrsOwner for BlockExpr {}
|
||||
impl ast::ModuleItemOwner for BlockExpr {}
|
||||
impl BlockExpr {
|
||||
pub fn label(&self) -> Option<Label> { support::child(&self.syntax) }
|
||||
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
|
||||
pub fn block(&self) -> Option<Block> { support::child(&self.syntax) }
|
||||
pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
|
||||
pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) }
|
||||
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -626,8 +631,8 @@ pub struct TryExpr {
|
|||
}
|
||||
impl ast::AttrsOwner for TryExpr {}
|
||||
impl TryExpr {
|
||||
pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) }
|
||||
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
pub fn question_mark_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![?]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -1120,19 +1125,6 @@ impl Condition {
|
|||
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Block {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl ast::AttrsOwner for Block {}
|
||||
impl ast::ModuleItemOwner for Block {}
|
||||
impl Block {
|
||||
pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
|
||||
pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) }
|
||||
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ParamList {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
|
@ -1249,6 +1241,9 @@ pub struct PathSegment {
|
|||
}
|
||||
impl PathSegment {
|
||||
pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) }
|
||||
pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) }
|
||||
pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
|
||||
pub fn super_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![super]) }
|
||||
pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
|
||||
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
|
||||
pub fn type_arg_list(&self) -> Option<TypeArgList> { support::child(&self.syntax) }
|
||||
|
@ -1473,7 +1468,7 @@ pub enum Expr {
|
|||
FieldExpr(FieldExpr),
|
||||
AwaitExpr(AwaitExpr),
|
||||
TryExpr(TryExpr),
|
||||
TryBlockExpr(TryBlockExpr),
|
||||
EffectExpr(EffectExpr),
|
||||
CastExpr(CastExpr),
|
||||
RefExpr(RefExpr),
|
||||
PrefixExpr(PrefixExpr),
|
||||
|
@ -1956,8 +1951,8 @@ impl AstNode for LoopExpr {
|
|||
}
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for TryBlockExpr {
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == TRY_BLOCK_EXPR }
|
||||
impl AstNode for EffectExpr {
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == EFFECT_EXPR }
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
|
@ -2649,17 +2644,6 @@ impl AstNode for Condition {
|
|||
}
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for Block {
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == BLOCK }
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for ParamList {
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM_LIST }
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
|
@ -3308,8 +3292,8 @@ impl From<AwaitExpr> for Expr {
|
|||
impl From<TryExpr> for Expr {
|
||||
fn from(node: TryExpr) -> Expr { Expr::TryExpr(node) }
|
||||
}
|
||||
impl From<TryBlockExpr> for Expr {
|
||||
fn from(node: TryBlockExpr) -> Expr { Expr::TryBlockExpr(node) }
|
||||
impl From<EffectExpr> for Expr {
|
||||
fn from(node: EffectExpr) -> Expr { Expr::EffectExpr(node) }
|
||||
}
|
||||
impl From<CastExpr> for Expr {
|
||||
fn from(node: CastExpr) -> Expr { Expr::CastExpr(node) }
|
||||
|
@ -3341,9 +3325,10 @@ impl AstNode for Expr {
|
|||
TUPLE_EXPR | ARRAY_EXPR | PAREN_EXPR | PATH_EXPR | LAMBDA_EXPR | IF_EXPR
|
||||
| LOOP_EXPR | FOR_EXPR | WHILE_EXPR | CONTINUE_EXPR | BREAK_EXPR | LABEL
|
||||
| BLOCK_EXPR | RETURN_EXPR | MATCH_EXPR | RECORD_LIT | CALL_EXPR | INDEX_EXPR
|
||||
| METHOD_CALL_EXPR | FIELD_EXPR | AWAIT_EXPR | TRY_EXPR | TRY_BLOCK_EXPR
|
||||
| CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR | LITERAL | MACRO_CALL
|
||||
| BOX_EXPR => true,
|
||||
| METHOD_CALL_EXPR | FIELD_EXPR | AWAIT_EXPR | TRY_EXPR | EFFECT_EXPR | CAST_EXPR
|
||||
| REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR | LITERAL | MACRO_CALL | BOX_EXPR => {
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -3371,7 +3356,7 @@ impl AstNode for Expr {
|
|||
FIELD_EXPR => Expr::FieldExpr(FieldExpr { syntax }),
|
||||
AWAIT_EXPR => Expr::AwaitExpr(AwaitExpr { syntax }),
|
||||
TRY_EXPR => Expr::TryExpr(TryExpr { syntax }),
|
||||
TRY_BLOCK_EXPR => Expr::TryBlockExpr(TryBlockExpr { syntax }),
|
||||
EFFECT_EXPR => Expr::EffectExpr(EffectExpr { syntax }),
|
||||
CAST_EXPR => Expr::CastExpr(CastExpr { syntax }),
|
||||
REF_EXPR => Expr::RefExpr(RefExpr { syntax }),
|
||||
PREFIX_EXPR => Expr::PrefixExpr(PrefixExpr { syntax }),
|
||||
|
@ -3408,7 +3393,7 @@ impl AstNode for Expr {
|
|||
Expr::FieldExpr(it) => &it.syntax,
|
||||
Expr::AwaitExpr(it) => &it.syntax,
|
||||
Expr::TryExpr(it) => &it.syntax,
|
||||
Expr::TryBlockExpr(it) => &it.syntax,
|
||||
Expr::EffectExpr(it) => &it.syntax,
|
||||
Expr::CastExpr(it) => &it.syntax,
|
||||
Expr::RefExpr(it) => &it.syntax,
|
||||
Expr::PrefixExpr(it) => &it.syntax,
|
||||
|
@ -3889,7 +3874,7 @@ impl std::fmt::Display for LoopExpr {
|
|||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for TryBlockExpr {
|
||||
impl std::fmt::Display for EffectExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
|
@ -4204,11 +4189,6 @@ impl std::fmt::Display for Condition {
|
|||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for Block {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for ParamList {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
|
|
|
@ -22,8 +22,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
|
|||
pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
|
||||
path_from_text(&format!("{}::{}", qual, segment))
|
||||
}
|
||||
|
||||
pub fn path_from_text(text: &str) -> ast::Path {
|
||||
fn path_from_text(text: &str) -> ast::Path {
|
||||
ast_from_text(text)
|
||||
}
|
||||
|
||||
|
@ -83,14 +82,6 @@ pub fn block_expr(
|
|||
ast_from_text(&format!("fn f() {}", buf))
|
||||
}
|
||||
|
||||
pub fn block_from_expr(e: ast::Expr) -> ast::Block {
|
||||
return from_text(&format!("{{ {} }}", e));
|
||||
|
||||
fn from_text(text: &str) -> ast::Block {
|
||||
ast_from_text(&format!("fn f() {}", text))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expr_unit() -> ast::Expr {
|
||||
expr_from_text("()")
|
||||
}
|
||||
|
|
|
@ -13,7 +13,12 @@ impl Comment {
|
|||
}
|
||||
|
||||
pub fn prefix(&self) -> &'static str {
|
||||
prefix_by_kind(self.kind())
|
||||
for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() {
|
||||
if *k == self.kind() && self.text().starts_with(prefix) {
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +53,7 @@ pub enum CommentPlacement {
|
|||
const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = {
|
||||
use {CommentPlacement::*, CommentShape::*};
|
||||
&[
|
||||
("////", CommentKind { shape: Line, doc: None }),
|
||||
("///", CommentKind { shape: Line, doc: Some(Outer) }),
|
||||
("//!", CommentKind { shape: Line, doc: Some(Inner) }),
|
||||
("/**", CommentKind { shape: Block, doc: Some(Outer) }),
|
||||
|
@ -69,15 +75,6 @@ fn kind_by_prefix(text: &str) -> CommentKind {
|
|||
panic!("bad comment text: {:?}", text)
|
||||
}
|
||||
|
||||
fn prefix_by_kind(kind: CommentKind) -> &'static str {
|
||||
for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() {
|
||||
if *k == kind {
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
impl Whitespace {
|
||||
pub fn spans_multiple_lines(&self) -> bool {
|
||||
let text = self.text();
|
||||
|
|
|
@ -237,8 +237,7 @@ fn api_walkthrough() {
|
|||
|
||||
// Let's get the `1 + 1` expression!
|
||||
let body: ast::BlockExpr = func.body().unwrap();
|
||||
let block = body.block().unwrap();
|
||||
let expr: ast::Expr = block.expr().unwrap();
|
||||
let expr: ast::Expr = body.expr().unwrap();
|
||||
|
||||
// Enums are used to group related ast nodes together, and can be used for
|
||||
// matching. However, because there are no public fields, it's possible to
|
||||
|
@ -274,8 +273,8 @@ fn api_walkthrough() {
|
|||
assert_eq!(text.to_string(), "1 + 1");
|
||||
|
||||
// There's a bunch of traversal methods on `SyntaxNode`:
|
||||
assert_eq!(expr_syntax.parent().as_ref(), Some(block.syntax()));
|
||||
assert_eq!(block.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
|
||||
assert_eq!(expr_syntax.parent().as_ref(), Some(body.syntax()));
|
||||
assert_eq!(body.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
|
||||
assert_eq!(
|
||||
expr_syntax.next_sibling_or_token().map(|it| it.kind()),
|
||||
Some(SyntaxKind::WHITESPACE)
|
||||
|
|
|
@ -180,7 +180,7 @@ fn rustc_token_kind_to_syntax_kind(
|
|||
return (syntax_kind, None);
|
||||
|
||||
fn match_literal_kind(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<&'static str>) {
|
||||
use rustc_lexer::LiteralKind as LK;
|
||||
use rustc_lexer::{LexRawStrError, LiteralKind as LK};
|
||||
|
||||
#[rustfmt::skip]
|
||||
let syntax_kind = match *kind {
|
||||
|
@ -215,21 +215,28 @@ fn rustc_token_kind_to_syntax_kind(
|
|||
return (BYTE_STRING, Some("Missing trailing `\"` symbol to terminate the byte string literal"))
|
||||
}
|
||||
|
||||
LK::RawStr { started: true, terminated: true, .. } => RAW_STRING,
|
||||
LK::RawStr { started: true, terminated: false, .. } => {
|
||||
return (RAW_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw string literal"))
|
||||
}
|
||||
LK::RawStr { started: false, .. } => {
|
||||
return (RAW_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw string literal"))
|
||||
}
|
||||
LK::RawStr(str) => match str.validate() {
|
||||
Ok(_) => RAW_STRING,
|
||||
Err(LexRawStrError::InvalidStarter) => return (RAW_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw string literal")),
|
||||
Err(LexRawStrError::NoTerminator { expected, found, .. }) => if expected == found {
|
||||
return (RAW_STRING, Some("Missing trailing `\"` to terminate the raw string literal"))
|
||||
} else {
|
||||
return (RAW_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw string literal"))
|
||||
|
||||
LK::RawByteStr { started: true, terminated: true, .. } => RAW_BYTE_STRING,
|
||||
LK::RawByteStr { started: true, terminated: false, .. } => {
|
||||
return (RAW_BYTE_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw byte string literal"))
|
||||
}
|
||||
LK::RawByteStr { started: false, .. } => {
|
||||
return (RAW_BYTE_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw byte string literal"))
|
||||
}
|
||||
},
|
||||
Err(LexRawStrError::TooManyDelimiters { .. }) => return (RAW_STRING, Some("Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols")),
|
||||
},
|
||||
LK::RawByteStr(str) => match str.validate() {
|
||||
Ok(_) => RAW_BYTE_STRING,
|
||||
Err(LexRawStrError::InvalidStarter) => return (RAW_BYTE_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw byte string literal")),
|
||||
Err(LexRawStrError::NoTerminator { expected, found, .. }) => if expected == found {
|
||||
return (RAW_BYTE_STRING, Some("Missing trailing `\"` to terminate the raw byte string literal"))
|
||||
} else {
|
||||
return (RAW_BYTE_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw byte string literal"))
|
||||
|
||||
},
|
||||
Err(LexRawStrError::TooManyDelimiters { .. }) => return (RAW_BYTE_STRING, Some("Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols")),
|
||||
},
|
||||
};
|
||||
|
||||
(syntax_kind, None)
|
||||
|
|
|
@ -96,6 +96,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
|
|||
ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors),
|
||||
ast::Visibility(it) => validate_visibility(it, &mut errors),
|
||||
ast::RangeExpr(it) => validate_range_expr(it, &mut errors),
|
||||
ast::PathSegment(it) => validate_path_keywords(it, &mut errors),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -222,3 +223,83 @@ fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) {
|
|||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxError>) {
|
||||
use ast::PathSegmentKind;
|
||||
|
||||
let path = segment.parent_path();
|
||||
let is_path_start = segment.coloncolon_token().is_none() && path.qualifier().is_none();
|
||||
|
||||
if let Some(token) = segment.self_token() {
|
||||
if !is_path_start {
|
||||
errors.push(SyntaxError::new(
|
||||
"The `self` keyword is only allowed as the first segment of a path",
|
||||
token.text_range(),
|
||||
));
|
||||
}
|
||||
} else if let Some(token) = segment.crate_token() {
|
||||
if !is_path_start || use_prefix(path).is_some() {
|
||||
errors.push(SyntaxError::new(
|
||||
"The `crate` keyword is only allowed as the first segment of a path",
|
||||
token.text_range(),
|
||||
));
|
||||
}
|
||||
} else if let Some(token) = segment.super_token() {
|
||||
if !all_supers(&path) {
|
||||
errors.push(SyntaxError::new(
|
||||
"The `super` keyword may only be preceded by other `super`s",
|
||||
token.text_range(),
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
let mut curr_path = path;
|
||||
while let Some(prefix) = use_prefix(curr_path) {
|
||||
if !all_supers(&prefix) {
|
||||
errors.push(SyntaxError::new(
|
||||
"The `super` keyword may only be preceded by other `super`s",
|
||||
token.text_range(),
|
||||
));
|
||||
return;
|
||||
}
|
||||
curr_path = prefix;
|
||||
}
|
||||
}
|
||||
|
||||
fn use_prefix(mut path: ast::Path) -> Option<ast::Path> {
|
||||
for node in path.syntax().ancestors().skip(1) {
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::UseTree(it) => if let Some(tree_path) = it.path() {
|
||||
// Even a top-level path exists within a `UseTree` so we must explicitly
|
||||
// allow our path but disallow anything else
|
||||
if tree_path != path {
|
||||
return Some(tree_path);
|
||||
}
|
||||
},
|
||||
ast::UseTreeList(_it) => continue,
|
||||
ast::Path(parent) => path = parent,
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn all_supers(path: &ast::Path) -> bool {
|
||||
let segment = match path.segment() {
|
||||
Some(it) => it,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if segment.kind() != Some(PathSegmentKind::SuperKw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(ref subpath) = path.qualifier() {
|
||||
return all_supers(subpath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,19 +6,17 @@ use crate::{
|
|||
SyntaxKind::*,
|
||||
};
|
||||
|
||||
pub(crate) fn validate_block_expr(expr: ast::BlockExpr, errors: &mut Vec<SyntaxError>) {
|
||||
if let Some(parent) = expr.syntax().parent() {
|
||||
pub(crate) fn validate_block_expr(block: ast::BlockExpr, errors: &mut Vec<SyntaxError>) {
|
||||
if let Some(parent) = block.syntax().parent() {
|
||||
match parent.kind() {
|
||||
FN_DEF | EXPR_STMT | BLOCK => return,
|
||||
FN_DEF | EXPR_STMT | BLOCK_EXPR => return,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(block) = expr.block() {
|
||||
errors.extend(block.attrs().map(|attr| {
|
||||
SyntaxError::new(
|
||||
"A block in this position cannot accept inner attributes",
|
||||
attr.syntax().text_range(),
|
||||
)
|
||||
}))
|
||||
}
|
||||
errors.extend(block.attrs().map(|attr| {
|
||||
SyntaxError::new(
|
||||
"A block in this position cannot accept inner attributes",
|
||||
attr.syntax().text_range(),
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -28,10 +28,9 @@ SOURCE_FILE@0..54
|
|||
R_PAREN@26..27 ")"
|
||||
WHITESPACE@27..28 " "
|
||||
BLOCK_EXPR@28..31
|
||||
BLOCK@28..31
|
||||
L_CURLY@28..29 "{"
|
||||
WHITESPACE@29..30 "\n"
|
||||
R_CURLY@30..31 "}"
|
||||
L_CURLY@28..29 "{"
|
||||
WHITESPACE@29..30 "\n"
|
||||
R_CURLY@30..31 "}"
|
||||
WHITESPACE@31..34 "\n\n\n"
|
||||
ATTR@34..53
|
||||
POUND@34..35 "#"
|
||||
|
|
|
@ -21,9 +21,8 @@ SOURCE_FILE@0..31
|
|||
L_PAREN@23..24 "("
|
||||
R_PAREN@24..25 ")"
|
||||
BLOCK_EXPR@25..27
|
||||
BLOCK@25..27
|
||||
L_CURLY@25..26 "{"
|
||||
R_CURLY@26..27 "}"
|
||||
L_CURLY@25..26 "{"
|
||||
R_CURLY@26..27 "}"
|
||||
WHITESPACE@27..29 "\n\n"
|
||||
ERROR@29..30
|
||||
R_CURLY@29..30 "}"
|
||||
|
|
|
@ -9,10 +9,9 @@ SOURCE_FILE@0..95
|
|||
R_PAREN@7..8 ")"
|
||||
WHITESPACE@8..9 " "
|
||||
BLOCK_EXPR@9..12
|
||||
BLOCK@9..12
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..11 "\n"
|
||||
R_CURLY@11..12 "}"
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..11 "\n"
|
||||
R_CURLY@11..12 "}"
|
||||
WHITESPACE@12..14 "\n\n"
|
||||
MACRO_CALL@14..19
|
||||
PATH@14..17
|
||||
|
@ -34,30 +33,28 @@ SOURCE_FILE@0..95
|
|||
TRUE_KW@29..33 "true"
|
||||
WHITESPACE@33..34 " "
|
||||
BLOCK_EXPR@34..51
|
||||
BLOCK@34..51
|
||||
L_CURLY@34..35 "{"
|
||||
WHITESPACE@35..44 "\n "
|
||||
LITERAL@44..45
|
||||
INT_NUMBER@44..45 "1"
|
||||
WHITESPACE@45..50 "\n "
|
||||
R_CURLY@50..51 "}"
|
||||
L_CURLY@34..35 "{"
|
||||
WHITESPACE@35..44 "\n "
|
||||
LITERAL@44..45
|
||||
INT_NUMBER@44..45 "1"
|
||||
WHITESPACE@45..50 "\n "
|
||||
R_CURLY@50..51 "}"
|
||||
WHITESPACE@51..52 " "
|
||||
ELSE_KW@52..56 "else"
|
||||
WHITESPACE@56..57 " "
|
||||
BLOCK_EXPR@57..78
|
||||
BLOCK@57..78
|
||||
L_CURLY@57..58 "{"
|
||||
WHITESPACE@58..67 "\n "
|
||||
BIN_EXPR@67..72
|
||||
LITERAL@67..68
|
||||
INT_NUMBER@67..68 "2"
|
||||
WHITESPACE@68..69 " "
|
||||
PLUS@69..70 "+"
|
||||
WHITESPACE@70..71 " "
|
||||
LITERAL@71..72
|
||||
INT_NUMBER@71..72 "3"
|
||||
WHITESPACE@72..77 "\n "
|
||||
R_CURLY@77..78 "}"
|
||||
L_CURLY@57..58 "{"
|
||||
WHITESPACE@58..67 "\n "
|
||||
BIN_EXPR@67..72
|
||||
LITERAL@67..68
|
||||
INT_NUMBER@67..68 "2"
|
||||
WHITESPACE@68..69 " "
|
||||
PLUS@69..70 "+"
|
||||
WHITESPACE@70..71 " "
|
||||
LITERAL@71..72
|
||||
INT_NUMBER@71..72 "3"
|
||||
WHITESPACE@72..77 "\n "
|
||||
R_CURLY@77..78 "}"
|
||||
WHITESPACE@78..79 "\n"
|
||||
R_CURLY@79..80 "}"
|
||||
WHITESPACE@80..82 "\n\n"
|
||||
|
@ -71,10 +68,9 @@ SOURCE_FILE@0..95
|
|||
R_PAREN@89..90 ")"
|
||||
WHITESPACE@90..91 " "
|
||||
BLOCK_EXPR@91..94
|
||||
BLOCK@91..94
|
||||
L_CURLY@91..92 "{"
|
||||
WHITESPACE@92..93 "\n"
|
||||
R_CURLY@93..94 "}"
|
||||
L_CURLY@91..92 "{"
|
||||
WHITESPACE@92..93 "\n"
|
||||
R_CURLY@93..94 "}"
|
||||
WHITESPACE@94..95 "\n"
|
||||
error 17..17: expected BANG
|
||||
error 19..19: expected SEMICOLON
|
||||
|
|
|
@ -9,37 +9,36 @@ SOURCE_FILE@0..42
|
|||
R_PAREN@8..9 ")"
|
||||
WHITESPACE@9..10 " "
|
||||
BLOCK_EXPR@10..41
|
||||
BLOCK@10..41
|
||||
L_CURLY@10..11 "{"
|
||||
WHITESPACE@11..16 "\n "
|
||||
EXPR_STMT@16..24
|
||||
LAMBDA_EXPR@16..24
|
||||
PARAM_LIST@16..18
|
||||
PIPE@16..17 "|"
|
||||
PIPE@17..18 "|"
|
||||
WHITESPACE@18..19 " "
|
||||
RET_TYPE@19..24
|
||||
THIN_ARROW@19..21 "->"
|
||||
WHITESPACE@21..22 " "
|
||||
TUPLE_TYPE@22..24
|
||||
L_PAREN@22..23 "("
|
||||
R_PAREN@23..24 ")"
|
||||
WHITESPACE@24..25 " "
|
||||
EXPR_STMT@25..39
|
||||
BLOCK_EXPR@25..38
|
||||
UNSAFE_KW@25..31 "unsafe"
|
||||
WHITESPACE@31..32 " "
|
||||
BLOCK@32..38
|
||||
L_CURLY@32..33 "{"
|
||||
WHITESPACE@33..34 " "
|
||||
TUPLE_EXPR@34..36
|
||||
L_PAREN@34..35 "("
|
||||
R_PAREN@35..36 ")"
|
||||
WHITESPACE@36..37 " "
|
||||
R_CURLY@37..38 "}"
|
||||
SEMICOLON@38..39 ";"
|
||||
WHITESPACE@39..40 "\n"
|
||||
R_CURLY@40..41 "}"
|
||||
L_CURLY@10..11 "{"
|
||||
WHITESPACE@11..16 "\n "
|
||||
EXPR_STMT@16..24
|
||||
LAMBDA_EXPR@16..24
|
||||
PARAM_LIST@16..18
|
||||
PIPE@16..17 "|"
|
||||
PIPE@17..18 "|"
|
||||
WHITESPACE@18..19 " "
|
||||
RET_TYPE@19..24
|
||||
THIN_ARROW@19..21 "->"
|
||||
WHITESPACE@21..22 " "
|
||||
TUPLE_TYPE@22..24
|
||||
L_PAREN@22..23 "("
|
||||
R_PAREN@23..24 ")"
|
||||
WHITESPACE@24..25 " "
|
||||
EXPR_STMT@25..39
|
||||
EFFECT_EXPR@25..38
|
||||
UNSAFE_KW@25..31 "unsafe"
|
||||
WHITESPACE@31..32 " "
|
||||
BLOCK_EXPR@32..38
|
||||
L_CURLY@32..33 "{"
|
||||
WHITESPACE@33..34 " "
|
||||
TUPLE_EXPR@34..36
|
||||
L_PAREN@34..35 "("
|
||||
R_PAREN@35..36 ")"
|
||||
WHITESPACE@36..37 " "
|
||||
R_CURLY@37..38 "}"
|
||||
SEMICOLON@38..39 ";"
|
||||
WHITESPACE@39..40 "\n"
|
||||
R_CURLY@40..41 "}"
|
||||
WHITESPACE@41..42 "\n"
|
||||
error 24..24: expected `{`
|
||||
error 24..24: expected a block
|
||||
error 24..24: expected SEMICOLON
|
||||
|
|
|
@ -25,8 +25,7 @@ SOURCE_FILE@0..23
|
|||
IDENT@18..19 "T"
|
||||
WHITESPACE@19..20 " "
|
||||
BLOCK_EXPR@20..22
|
||||
BLOCK@20..22
|
||||
L_CURLY@20..21 "{"
|
||||
R_CURLY@21..22 "}"
|
||||
L_CURLY@20..21 "{"
|
||||
R_CURLY@21..22 "}"
|
||||
WHITESPACE@22..23 "\n"
|
||||
error 19..19: expected colon
|
||||
|
|
|
@ -9,36 +9,35 @@ SOURCE_FILE@0..56
|
|||
R_PAREN@7..8 ")"
|
||||
WHITESPACE@8..9 " "
|
||||
BLOCK_EXPR@9..55
|
||||
BLOCK@9..55
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..15 "\n "
|
||||
EXPR_STMT@15..38
|
||||
CALL_EXPR@15..38
|
||||
PATH_EXPR@15..18
|
||||
PATH@15..18
|
||||
PATH_SEGMENT@15..18
|
||||
NAME_REF@15..18
|
||||
IDENT@15..18 "foo"
|
||||
ARG_LIST@18..38
|
||||
L_PAREN@18..19 "("
|
||||
WHITESPACE@19..28 "\n "
|
||||
LITERAL@28..29
|
||||
INT_NUMBER@28..29 "1"
|
||||
COMMA@29..30 ","
|
||||
WHITESPACE@30..31 " "
|
||||
LITERAL@31..32
|
||||
INT_NUMBER@31..32 "2"
|
||||
WHITESPACE@32..37 "\n "
|
||||
R_PAREN@37..38 ")"
|
||||
WHITESPACE@38..43 "\n "
|
||||
EXPR_STMT@43..53
|
||||
RETURN_EXPR@43..52
|
||||
RETURN_KW@43..49 "return"
|
||||
WHITESPACE@49..50 " "
|
||||
LITERAL@50..52
|
||||
INT_NUMBER@50..52 "92"
|
||||
SEMICOLON@52..53 ";"
|
||||
WHITESPACE@53..54 "\n"
|
||||
R_CURLY@54..55 "}"
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..15 "\n "
|
||||
EXPR_STMT@15..38
|
||||
CALL_EXPR@15..38
|
||||
PATH_EXPR@15..18
|
||||
PATH@15..18
|
||||
PATH_SEGMENT@15..18
|
||||
NAME_REF@15..18
|
||||
IDENT@15..18 "foo"
|
||||
ARG_LIST@18..38
|
||||
L_PAREN@18..19 "("
|
||||
WHITESPACE@19..28 "\n "
|
||||
LITERAL@28..29
|
||||
INT_NUMBER@28..29 "1"
|
||||
COMMA@29..30 ","
|
||||
WHITESPACE@30..31 " "
|
||||
LITERAL@31..32
|
||||
INT_NUMBER@31..32 "2"
|
||||
WHITESPACE@32..37 "\n "
|
||||
R_PAREN@37..38 ")"
|
||||
WHITESPACE@38..43 "\n "
|
||||
EXPR_STMT@43..53
|
||||
RETURN_EXPR@43..52
|
||||
RETURN_KW@43..49 "return"
|
||||
WHITESPACE@49..50 " "
|
||||
LITERAL@50..52
|
||||
INT_NUMBER@50..52 "92"
|
||||
SEMICOLON@52..53 ";"
|
||||
WHITESPACE@53..54 "\n"
|
||||
R_CURLY@54..55 "}"
|
||||
WHITESPACE@55..56 "\n"
|
||||
error 38..38: expected SEMICOLON
|
||||
|
|
|
@ -20,28 +20,27 @@ SOURCE_FILE@0..47
|
|||
R_PAREN@15..16 ")"
|
||||
WHITESPACE@16..17 " "
|
||||
BLOCK_EXPR@17..46
|
||||
BLOCK@17..46
|
||||
L_CURLY@17..18 "{"
|
||||
WHITESPACE@18..23 "\n "
|
||||
LET_STMT@23..36
|
||||
LET_KW@23..26 "let"
|
||||
WHITESPACE@26..27 " "
|
||||
BIND_PAT@27..30
|
||||
NAME@27..30
|
||||
IDENT@27..30 "bar"
|
||||
WHITESPACE@30..31 " "
|
||||
EQ@31..32 "="
|
||||
WHITESPACE@32..33 " "
|
||||
LITERAL@33..35
|
||||
INT_NUMBER@33..35 "92"
|
||||
SEMICOLON@35..36 ";"
|
||||
WHITESPACE@36..41 "\n "
|
||||
BIN_EXPR@41..44
|
||||
LITERAL@41..42
|
||||
INT_NUMBER@41..42 "1"
|
||||
WHITESPACE@42..43 " "
|
||||
PLUS@43..44 "+"
|
||||
WHITESPACE@44..45 "\n"
|
||||
R_CURLY@45..46 "}"
|
||||
L_CURLY@17..18 "{"
|
||||
WHITESPACE@18..23 "\n "
|
||||
LET_STMT@23..36
|
||||
LET_KW@23..26 "let"
|
||||
WHITESPACE@26..27 " "
|
||||
BIND_PAT@27..30
|
||||
NAME@27..30
|
||||
IDENT@27..30 "bar"
|
||||
WHITESPACE@30..31 " "
|
||||
EQ@31..32 "="
|
||||
WHITESPACE@32..33 " "
|
||||
LITERAL@33..35
|
||||
INT_NUMBER@33..35 "92"
|
||||
SEMICOLON@35..36 ";"
|
||||
WHITESPACE@36..41 "\n "
|
||||
BIN_EXPR@41..44
|
||||
LITERAL@41..42
|
||||
INT_NUMBER@41..42 "1"
|
||||
WHITESPACE@42..43 " "
|
||||
PLUS@43..44 "+"
|
||||
WHITESPACE@44..45 "\n"
|
||||
R_CURLY@45..46 "}"
|
||||
WHITESPACE@46..47 "\n"
|
||||
error 44..44: expected expression
|
||||
|
|
|
@ -33,89 +33,88 @@ SOURCE_FILE@0..183
|
|||
IDENT@39..46 "ScopeId"
|
||||
WHITESPACE@46..47 " "
|
||||
BLOCK_EXPR@47..161
|
||||
BLOCK@47..161
|
||||
L_CURLY@47..48 "{"
|
||||
WHITESPACE@48..57 "\n "
|
||||
LET_STMT@57..85
|
||||
LET_KW@57..60 "let"
|
||||
WHITESPACE@60..61 " "
|
||||
BIND_PAT@61..64
|
||||
NAME@61..64
|
||||
IDENT@61..64 "res"
|
||||
WHITESPACE@64..65 " "
|
||||
EQ@65..66 "="
|
||||
WHITESPACE@66..67 " "
|
||||
METHOD_CALL_EXPR@67..84
|
||||
FIELD_EXPR@67..78
|
||||
PATH_EXPR@67..71
|
||||
PATH@67..71
|
||||
PATH_SEGMENT@67..71
|
||||
SELF_KW@67..71 "self"
|
||||
DOT@71..72 "."
|
||||
NAME_REF@72..78
|
||||
IDENT@72..78 "scopes"
|
||||
DOT@78..79 "."
|
||||
NAME_REF@79..82
|
||||
IDENT@79..82 "len"
|
||||
ARG_LIST@82..84
|
||||
L_PAREN@82..83 "("
|
||||
R_PAREN@83..84 ")"
|
||||
SEMICOLON@84..85 ";"
|
||||
WHITESPACE@85..94 "\n "
|
||||
METHOD_CALL_EXPR@94..155
|
||||
FIELD_EXPR@94..105
|
||||
PATH_EXPR@94..98
|
||||
PATH@94..98
|
||||
PATH_SEGMENT@94..98
|
||||
SELF_KW@94..98 "self"
|
||||
DOT@98..99 "."
|
||||
NAME_REF@99..105
|
||||
IDENT@99..105 "scopes"
|
||||
DOT@105..106 "."
|
||||
NAME_REF@106..110
|
||||
IDENT@106..110 "push"
|
||||
ARG_LIST@110..155
|
||||
L_PAREN@110..111 "("
|
||||
RECORD_LIT@111..154
|
||||
PATH@111..120
|
||||
PATH_SEGMENT@111..120
|
||||
NAME_REF@111..120
|
||||
IDENT@111..120 "ScopeData"
|
||||
WHITESPACE@120..121 " "
|
||||
RECORD_FIELD_LIST@121..154
|
||||
L_CURLY@121..122 "{"
|
||||
WHITESPACE@122..123 " "
|
||||
RECORD_FIELD@123..135
|
||||
NAME_REF@123..129
|
||||
IDENT@123..129 "parent"
|
||||
COLON@129..130 ":"
|
||||
WHITESPACE@130..131 " "
|
||||
PATH_EXPR@131..135
|
||||
PATH@131..135
|
||||
PATH_SEGMENT@131..135
|
||||
NAME_REF@131..135
|
||||
IDENT@131..135 "None"
|
||||
COMMA@135..136 ","
|
||||
WHITESPACE@136..137 " "
|
||||
RECORD_FIELD@137..152
|
||||
NAME_REF@137..144
|
||||
IDENT@137..144 "entries"
|
||||
COLON@144..145 ":"
|
||||
WHITESPACE@145..146 " "
|
||||
MACRO_CALL@146..152
|
||||
PATH@146..149
|
||||
PATH_SEGMENT@146..149
|
||||
NAME_REF@146..149
|
||||
IDENT@146..149 "vec"
|
||||
BANG@149..150 "!"
|
||||
TOKEN_TREE@150..152
|
||||
L_BRACK@150..151 "["
|
||||
R_BRACK@151..152 "]"
|
||||
WHITESPACE@152..153 " "
|
||||
R_CURLY@153..154 "}"
|
||||
R_PAREN@154..155 ")"
|
||||
WHITESPACE@155..160 "\n "
|
||||
R_CURLY@160..161 "}"
|
||||
L_CURLY@47..48 "{"
|
||||
WHITESPACE@48..57 "\n "
|
||||
LET_STMT@57..85
|
||||
LET_KW@57..60 "let"
|
||||
WHITESPACE@60..61 " "
|
||||
BIND_PAT@61..64
|
||||
NAME@61..64
|
||||
IDENT@61..64 "res"
|
||||
WHITESPACE@64..65 " "
|
||||
EQ@65..66 "="
|
||||
WHITESPACE@66..67 " "
|
||||
METHOD_CALL_EXPR@67..84
|
||||
FIELD_EXPR@67..78
|
||||
PATH_EXPR@67..71
|
||||
PATH@67..71
|
||||
PATH_SEGMENT@67..71
|
||||
SELF_KW@67..71 "self"
|
||||
DOT@71..72 "."
|
||||
NAME_REF@72..78
|
||||
IDENT@72..78 "scopes"
|
||||
DOT@78..79 "."
|
||||
NAME_REF@79..82
|
||||
IDENT@79..82 "len"
|
||||
ARG_LIST@82..84
|
||||
L_PAREN@82..83 "("
|
||||
R_PAREN@83..84 ")"
|
||||
SEMICOLON@84..85 ";"
|
||||
WHITESPACE@85..94 "\n "
|
||||
METHOD_CALL_EXPR@94..155
|
||||
FIELD_EXPR@94..105
|
||||
PATH_EXPR@94..98
|
||||
PATH@94..98
|
||||
PATH_SEGMENT@94..98
|
||||
SELF_KW@94..98 "self"
|
||||
DOT@98..99 "."
|
||||
NAME_REF@99..105
|
||||
IDENT@99..105 "scopes"
|
||||
DOT@105..106 "."
|
||||
NAME_REF@106..110
|
||||
IDENT@106..110 "push"
|
||||
ARG_LIST@110..155
|
||||
L_PAREN@110..111 "("
|
||||
RECORD_LIT@111..154
|
||||
PATH@111..120
|
||||
PATH_SEGMENT@111..120
|
||||
NAME_REF@111..120
|
||||
IDENT@111..120 "ScopeData"
|
||||
WHITESPACE@120..121 " "
|
||||
RECORD_FIELD_LIST@121..154
|
||||
L_CURLY@121..122 "{"
|
||||
WHITESPACE@122..123 " "
|
||||
RECORD_FIELD@123..135
|
||||
NAME_REF@123..129
|
||||
IDENT@123..129 "parent"
|
||||
COLON@129..130 ":"
|
||||
WHITESPACE@130..131 " "
|
||||
PATH_EXPR@131..135
|
||||
PATH@131..135
|
||||
PATH_SEGMENT@131..135
|
||||
NAME_REF@131..135
|
||||
IDENT@131..135 "None"
|
||||
COMMA@135..136 ","
|
||||
WHITESPACE@136..137 " "
|
||||
RECORD_FIELD@137..152
|
||||
NAME_REF@137..144
|
||||
IDENT@137..144 "entries"
|
||||
COLON@144..145 ":"
|
||||
WHITESPACE@145..146 " "
|
||||
MACRO_CALL@146..152
|
||||
PATH@146..149
|
||||
PATH_SEGMENT@146..149
|
||||
NAME_REF@146..149
|
||||
IDENT@146..149 "vec"
|
||||
BANG@149..150 "!"
|
||||
TOKEN_TREE@150..152
|
||||
L_BRACK@150..151 "["
|
||||
R_BRACK@151..152 "]"
|
||||
WHITESPACE@152..153 " "
|
||||
R_CURLY@153..154 "}"
|
||||
R_PAREN@154..155 ")"
|
||||
WHITESPACE@155..160 "\n "
|
||||
R_CURLY@160..161 "}"
|
||||
WHITESPACE@161..167 "\n\n "
|
||||
FN_DEF@167..180
|
||||
FN_KW@167..169 "fn"
|
||||
|
|
|
@ -9,91 +9,87 @@ SOURCE_FILE@0..139
|
|||
R_PAREN@7..8 ")"
|
||||
WHITESPACE@8..9 " "
|
||||
BLOCK_EXPR@9..138
|
||||
BLOCK@9..138
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..15 "\n "
|
||||
LET_STMT@15..24
|
||||
LET_KW@15..18 "let"
|
||||
WHITESPACE@18..19 " "
|
||||
BIND_PAT@19..22
|
||||
NAME@19..22
|
||||
IDENT@19..22 "foo"
|
||||
WHITESPACE@22..23 " "
|
||||
EQ@23..24 "="
|
||||
WHITESPACE@24..29 "\n "
|
||||
LET_STMT@29..41
|
||||
LET_KW@29..32 "let"
|
||||
WHITESPACE@32..33 " "
|
||||
BIND_PAT@33..36
|
||||
NAME@33..36
|
||||
IDENT@33..36 "bar"
|
||||
WHITESPACE@36..37 " "
|
||||
EQ@37..38 "="
|
||||
WHITESPACE@38..39 " "
|
||||
LITERAL@39..40
|
||||
INT_NUMBER@39..40 "1"
|
||||
SEMICOLON@40..41 ";"
|
||||
WHITESPACE@41..46 "\n "
|
||||
LET_STMT@46..49
|
||||
LET_KW@46..49 "let"
|
||||
WHITESPACE@49..54 "\n "
|
||||
LET_STMT@54..67
|
||||
LET_KW@54..57 "let"
|
||||
WHITESPACE@57..58 " "
|
||||
BIND_PAT@58..61
|
||||
NAME@58..61
|
||||
IDENT@58..61 "baz"
|
||||
WHITESPACE@61..62 " "
|
||||
EQ@62..63 "="
|
||||
WHITESPACE@63..64 " "
|
||||
LITERAL@64..66
|
||||
INT_NUMBER@64..66 "92"
|
||||
SEMICOLON@66..67 ";"
|
||||
WHITESPACE@67..72 "\n "
|
||||
LET_STMT@72..75
|
||||
LET_KW@72..75 "let"
|
||||
WHITESPACE@75..80 "\n "
|
||||
EXPR_STMT@80..90
|
||||
IF_EXPR@80..90
|
||||
IF_KW@80..82 "if"
|
||||
WHITESPACE@82..83 " "
|
||||
CONDITION@83..87
|
||||
LITERAL@83..87
|
||||
TRUE_KW@83..87 "true"
|
||||
WHITESPACE@87..88 " "
|
||||
BLOCK_EXPR@88..90
|
||||
BLOCK@88..90
|
||||
L_CURLY@88..89 "{"
|
||||
R_CURLY@89..90 "}"
|
||||
WHITESPACE@90..95 "\n "
|
||||
LET_STMT@95..98
|
||||
LET_KW@95..98 "let"
|
||||
WHITESPACE@98..103 "\n "
|
||||
EXPR_STMT@103..116
|
||||
WHILE_EXPR@103..116
|
||||
WHILE_KW@103..108 "while"
|
||||
WHITESPACE@108..109 " "
|
||||
CONDITION@109..113
|
||||
LITERAL@109..113
|
||||
TRUE_KW@109..113 "true"
|
||||
WHITESPACE@113..114 " "
|
||||
BLOCK_EXPR@114..116
|
||||
BLOCK@114..116
|
||||
L_CURLY@114..115 "{"
|
||||
R_CURLY@115..116 "}"
|
||||
WHITESPACE@116..121 "\n "
|
||||
LET_STMT@121..124
|
||||
LET_KW@121..124 "let"
|
||||
WHITESPACE@124..129 "\n "
|
||||
LOOP_EXPR@129..136
|
||||
LOOP_KW@129..133 "loop"
|
||||
WHITESPACE@133..134 " "
|
||||
BLOCK_EXPR@134..136
|
||||
BLOCK@134..136
|
||||
L_CURLY@134..135 "{"
|
||||
R_CURLY@135..136 "}"
|
||||
WHITESPACE@136..137 "\n"
|
||||
R_CURLY@137..138 "}"
|
||||
L_CURLY@9..10 "{"
|
||||
WHITESPACE@10..15 "\n "
|
||||
LET_STMT@15..24
|
||||
LET_KW@15..18 "let"
|
||||
WHITESPACE@18..19 " "
|
||||
BIND_PAT@19..22
|
||||
NAME@19..22
|
||||
IDENT@19..22 "foo"
|
||||
WHITESPACE@22..23 " "
|
||||
EQ@23..24 "="
|
||||
WHITESPACE@24..29 "\n "
|
||||
LET_STMT@29..41
|
||||
LET_KW@29..32 "let"
|
||||
WHITESPACE@32..33 " "
|
||||
BIND_PAT@33..36
|
||||
NAME@33..36
|
||||
IDENT@33..36 "bar"
|
||||
WHITESPACE@36..37 " "
|
||||
EQ@37..38 "="
|
||||
WHITESPACE@38..39 " "
|
||||
LITERAL@39..40
|
||||
INT_NUMBER@39..40 "1"
|
||||
SEMICOLON@40..41 ";"
|
||||
WHITESPACE@41..46 "\n "
|
||||
LET_STMT@46..49
|
||||
LET_KW@46..49 "let"
|
||||
WHITESPACE@49..54 "\n "
|
||||
LET_STMT@54..67
|
||||
LET_KW@54..57 "let"
|
||||
WHITESPACE@57..58 " "
|
||||
BIND_PAT@58..61
|
||||
NAME@58..61
|
||||
IDENT@58..61 "baz"
|
||||
WHITESPACE@61..62 " "
|
||||
EQ@62..63 "="
|
||||
WHITESPACE@63..64 " "
|
||||
LITERAL@64..66
|
||||
INT_NUMBER@64..66 "92"
|
||||
SEMICOLON@66..67 ";"
|
||||
WHITESPACE@67..72 "\n "
|
||||
LET_STMT@72..75
|
||||
LET_KW@72..75 "let"
|
||||
WHITESPACE@75..80 "\n "
|
||||
EXPR_STMT@80..90
|
||||
IF_EXPR@80..90
|
||||
IF_KW@80..82 "if"
|
||||
WHITESPACE@82..83 " "
|
||||
CONDITION@83..87
|
||||
LITERAL@83..87
|
||||
TRUE_KW@83..87 "true"
|
||||
WHITESPACE@87..88 " "
|
||||
BLOCK_EXPR@88..90
|
||||
L_CURLY@88..89 "{"
|
||||
R_CURLY@89..90 "}"
|
||||
WHITESPACE@90..95 "\n "
|
||||
LET_STMT@95..98
|
||||
LET_KW@95..98 "let"
|
||||
WHITESPACE@98..103 "\n "
|
||||
EXPR_STMT@103..116
|
||||
WHILE_EXPR@103..116
|
||||
WHILE_KW@103..108 "while"
|
||||
WHITESPACE@108..109 " "
|
||||
CONDITION@109..113
|
||||
LITERAL@109..113
|
||||
TRUE_KW@109..113 "true"
|
||||
WHITESPACE@113..114 " "
|
||||
BLOCK_EXPR@114..116
|
||||
L_CURLY@114..115 "{"
|
||||
R_CURLY@115..116 "}"
|
||||
WHITESPACE@116..121 "\n "
|
||||
LET_STMT@121..124
|
||||
LET_KW@121..124 "let"
|
||||
WHITESPACE@124..129 "\n "
|
||||
LOOP_EXPR@129..136
|
||||
LOOP_KW@129..133 "loop"
|
||||
WHITESPACE@133..134 " "
|
||||
BLOCK_EXPR@134..136
|
||||
L_CURLY@134..135 "{"
|
||||
R_CURLY@135..136 "}"
|
||||
WHITESPACE@136..137 "\n"
|
||||
R_CURLY@137..138 "}"
|
||||
WHITESPACE@138..139 "\n"
|
||||
error 24..24: expected expression
|
||||
error 24..24: expected SEMICOLON
|
||||
|
|
|
@ -12,9 +12,8 @@ SOURCE_FILE@0..16
|
|||
R_PAREN@11..12 ")"
|
||||
WHITESPACE@12..13 " "
|
||||
BLOCK_EXPR@13..15
|
||||
BLOCK@13..15
|
||||
L_CURLY@13..14 "{"
|
||||
R_CURLY@14..15 "}"
|
||||
L_CURLY@13..14 "{"
|
||||
R_CURLY@14..15 "}"
|
||||
WHITESPACE@15..16 "\n"
|
||||
error 2..2: expected a name
|
||||
error 2..2: expected function arguments
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue