diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2c1192f0728d..3f52f31f805d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -39,7 +39,6 @@ jobs: with: toolchain: stable profile: minimal - target: x86_64-unknown-linux-musl override: true - name: Install Nodejs diff --git a/.vscode/launch.json b/.vscode/launch.json index 3f74d75666d2..6a2fff906510 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -41,7 +41,7 @@ "outFiles": [ "${workspaceFolder}/editors/code/out/**/*.js" ], - "preLaunchTask": "Build Extension", + "preLaunchTask": "Build Server and Extension", "skipFiles": [ "/**/*.js" ], @@ -62,7 +62,7 @@ "outFiles": [ "${workspaceFolder}/editors/code/out/**/*.js" ], - "preLaunchTask": "Build Extension", + "preLaunchTask": "Build Server (Release) and Extension", "skipFiles": [ "/**/*.js" ], diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4037e7cce112..0969ce89a107 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -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" + } ] } diff --git a/Cargo.lock b/Cargo.lock index 367ff3f82812..5d50a766fe5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index 52b4c82db663..9ac65ab397fa 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs @@ -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 { diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index e4fa9ee366e4..6696cc832300 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs @@ -180,7 +180,9 @@ trait Trait { } impl Trait 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"); +} +"#####, + ) +} diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 03806724a3ac..49deb670175c 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs @@ -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 { _ => 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 for A { fn from(v: u32) -> Self { A::One(v) } } - -pub trait From { - fn from(T) -> Self; -}"#, +"#, ); } diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index 2d6d44980e18..e466c9a86d87 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs @@ -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 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 { fn foo(&self, t: T) -> &T; } struct S; -impl Foo for S { <|> }", - " +impl Foo for S { <|> }"#, + r#" trait Foo { fn foo(&self, t: T) -> &T; } struct S; impl Foo for S { - <|>fn foo(&self, t: u32) -> &u32 { todo!() } -}", + <|>fn foo(&self, t: u32) -> &u32 { + todo!() + } +}"#, ); } @@ -294,16 +312,18 @@ impl Foo for S { fn fill_in_type_params_2() { check_assist( add_missing_impl_members, - " + r#" trait Foo { fn foo(&self, t: T) -> &T; } struct S; -impl Foo for S { <|> }", - " +impl Foo for S { <|> }"#, + r#" trait Foo { fn foo(&self, t: T) -> &T; } struct S; impl Foo for S { - <|>fn foo(&self, t: U) -> &U { todo!() } -}", + <|>fn foo(&self, t: U) -> &U { + todo!() + } +}"#, ); } @@ -311,16 +331,18 @@ impl Foo 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; 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!() + } +}"#, ); } @@ -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; 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!() + } +}"#, ); } @@ -398,15 +426,15 @@ impl foo::Foo for S { // when substituting params, the substituted param should not be qualified! check_assist( add_missing_impl_members, - " + r#" mod foo { trait Foo { fn foo(&self, bar: T); } pub struct Param; } struct Param; struct S; -impl foo::Foo for S { <|> }", - " +impl foo::Foo for S { <|> }"#, + r#" mod foo { trait Foo { fn foo(&self, bar: T); } pub struct Param; @@ -414,8 +442,10 @@ mod foo { struct Param; struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: Param) { todo!() } -}", + <|>fn foo(&self, bar: Param) { + todo!() + } +}"#, ); } @@ -423,15 +453,15 @@ impl foo::Foo for S { fn test_qualify_path_associated_item() { check_assist( add_missing_impl_members, - " + r#" mod foo { pub struct Bar; impl Bar { type Assoc = u32; } trait Foo { fn foo(&self, bar: Bar::Assoc); } } struct S; -impl foo::Foo for S { <|> }", - " +impl foo::Foo for S { <|> }"#, + r#" mod foo { pub struct Bar; impl Bar { type Assoc = u32; } @@ -439,8 +469,10 @@ mod foo { } struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: foo::Bar::Assoc) { todo!() } -}", + <|>fn foo(&self, bar: foo::Bar::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; pub struct Baz; 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; pub struct Baz; @@ -464,8 +496,10 @@ mod foo { } struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: foo::Bar) { todo!() } -}", + <|>fn foo(&self, bar: foo::Bar) { + 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 { 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 { 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 } -}", +}"#, ) } } diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index ea6c56f8cffe..eede2fe91dec 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs @@ -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 { // 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 { }; 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 { 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 { } }; 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()); diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index f5702f6e0c22..60ec536a7c9d 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs @@ -89,6 +89,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option { | (ast::Expr::ParenExpr(_), _) | (ast::Expr::PathExpr(_), _) | (ast::Expr::BlockExpr(_), _) + | (ast::Expr::EffectExpr(_), _) | (_, ast::Expr::CallExpr(_)) | (_, ast::Expr::TupleExpr(_)) | (_, ast::Expr::ArrayExpr(_)) diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index eda9ac296304..39c656305982 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs @@ -111,7 +111,7 @@ fn valid_target_expr(node: SyntaxNode) -> Option { /// 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)); diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs index d5ccdd91cefa..b084dd9ee22c 100644 --- a/crates/ra_assists/src/handlers/move_guard.rs +++ b/crates/ra_assists/src/handlers/move_guard.rs @@ -113,9 +113,9 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option { "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()) } diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index 0a0a88f3dcff..9841f6980bc3 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs @@ -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 { 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 { #[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 { Some(T), None } +use Option::*; + +fn foo(x: Option) { + <|>if let Some(x) = x { + println!("{}", x) + } else { + println!("none") + } +} + "#, + r#" +enum Option { Some(T), None } +use Option::*; + +fn foo(x: Option) { + <|>match x { + Some(x) => println!("{}", x), + None => println!("none"), + } +} + "#, + ); + } + + #[test] + fn special_case_result() { + check_assist( + replace_if_let_with_match, + r#" +enum Result { Ok(T), Err(E) } +use Result::*; + +fn foo(x: Result) { + <|>if let Ok(x) = x { + println!("{}", x) + } else { + println!("none") + } +} + "#, + r#" +enum Result { Ok(T), Err(E) } +use Result::*; + +fn foo(x: Result) { + <|>match x { + Ok(x) => println!("{}", x), + Err(_) => println!("none"), + } +} + "#, + ); + } } diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index bdbaae389837..0cf23b754e8d 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs @@ -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 { 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))), diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index 2f02df3038c3..918e8dd8dae5 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs @@ -27,7 +27,7 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option 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; diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index 62cb7a7631ad..62d4ea522025 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs @@ -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 { } 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::(method_call.into(), match_expr); - }, - ); - } - } - None + edit.target(method_call.syntax().text_range()); + edit.set_cursor(caller.syntax().text_range().start()); + edit.replace_ast::(method_call.into(), match_expr); + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs new file mode 100644 index 000000000000..58649c47eb6d --- /dev/null +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -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 { + 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::>() + .join("\n"); + + edit.replace(expr.syntax().text_range(), expr_string); + }) +} + +fn extract_expr(cursor_range: TextRange, block: BlockExpr) -> Option { + 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"); + } + } + } + "#, + ); + } +} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 64bd87afbd47..c5df86600f51 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -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, diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs index 6c2a2b8b6ea6..8d910205f0ab 100644 --- a/crates/ra_assists/src/marks.rs +++ b/crates/ra_assists/src/marks.rs @@ -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 ]; diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 3d6c59bda91d..efd9886978a5 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs @@ -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 { _ => 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, ty: &Type) -> Option { + 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 { + 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 { + self.find_trait("core:convert:From") + } + + fn find_trait(&self, path: &str) -> Option { + 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, + } + } +} diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml index 76e5cada4f15..324c33d9dd28 100644 --- a/crates/ra_flycheck/Cargo.toml +++ b/crates/ra_flycheck/Cargo.toml @@ -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" diff --git a/crates/ra_fmt/src/lib.rs b/crates/ra_fmt/src/lib.rs index 0b4ba1bbe654..f910ded9da6f 100644 --- a/crates/ra_fmt/src/lib.rs +++ b/crates/ra_fmt/src/lib.rs @@ -42,7 +42,6 @@ pub fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { } pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option { - 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 { 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 diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index fb788736d86a..af59aa1b6cf4 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -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() } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 312ef3814b32..c5df4ac24da4 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -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}; diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 86bfb416c886..515e5eb1713b 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs @@ -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 { + 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( + &self, + db: &dyn HirDatabase, + mut cb: impl FnMut(TypeAlias) -> Option, + ) -> Option { + 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 { + 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 { self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat) } diff --git a/crates/ra_hir/src/semantics/source_to_def.rs b/crates/ra_hir/src/semantics/source_to_def.rs index 6f3b5b2da869..8af64fdc1b25 100644 --- a/crates/ra_hir/src/semantics/source_to_def.rs +++ b/crates/ra_hir/src/semantics/source_to_def.rs @@ -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 { diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index 74d64c97d314..c63d1b8473ef 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -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) } diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 753becc3df7b..8eef51828353 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -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>, ast: &InFile, ) -> 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())), }, ); diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index 5a86af8ba378..576cd0c65ba9 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs @@ -140,6 +140,7 @@ impl Attr { } } +#[derive(Debug, Clone, Copy)] pub struct AttrQuery<'a> { attrs: &'a Attrs, key: &'static str, diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 890cefcafbe4..4edaad9600af 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -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, diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 571603854386..687216dc35ea 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -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::from_src(ast, &self.hygiene) + } +} + pub(super) fn lower( db: &dyn DefDatabase, def: DefWithBodyId, @@ -42,10 +60,13 @@ pub(super) fn lower( params: Option, body: Option, ) -> (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) = match item { diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index ccb682f9abb2..7a2067e49cb7 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -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 { 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, ) -> 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 } diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs index d850244c4a1f..09a5241f7812 100644 --- a/crates/ra_hir_def/src/generics.rs +++ b/crates/ra_hir_def/src/generics.rs @@ -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) { 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 }); } diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 98c74fe257b4..bf3968bd62ac 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -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); } diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index 39b011ad724c..a71503c76ecd 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs @@ -188,6 +188,7 @@ pub(super) struct MacroData { pub(super) path: ModPath, pub(super) name: Option, 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)); } diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs index b0befdfbd614..9bc0e628747c 100644 --- a/crates/ra_hir_def/src/nameres/tests/macros.rs +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs @@ -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( diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 162b3c8c74fe..e84efe2abd04 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -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 { - lower::lower_generic_args(node) + pub(crate) fn from_ast(lower_ctx: &LowerCtx, node: ast::TypeArgList) -> Option { + lower::lower_generic_args(lower_ctx, node) } pub(crate) fn empty() -> GenericArgs { diff --git a/crates/ra_hir_def/src/path/lower.rs b/crates/ra_hir_def/src/path/lower.rs index 9ec2e0dcdf01..6a0c019fdff9 100644 --- a/crates/ra_hir_def/src/path/lower.rs +++ b/crates/ra_hir_def/src/path/lower.rs @@ -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 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 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 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 { // ::foo @@ -113,6 +116,21 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option } 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 } } -pub(super) fn lower_generic_args(node: ast::TypeArgList) -> Option { +pub(super) fn lower_generic_args( + lower_ctx: &LowerCtx, + node: ast::TypeArgList, +) -> Option { 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 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 /// 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, ret_type: Option, ) -> Option { @@ -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), diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs index f308c6bdfc3e..5bdad9efdef3 100644 --- a/crates/ra_hir_def/src/type_ref.rs +++ b/crates/ra_hir_def/src/type_ref.rs @@ -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) -> Self { + pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option) -> 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) -> Vec { +pub(crate) fn type_bounds_from_ast( + lower_ctx: &LowerCtx, + type_bounds_opt: Option, +) -> Vec { 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, }; diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs index e60f879a3935..1dc9cac6651e 100644 --- a/crates/ra_hir_expand/src/builtin_derive.rs +++ b/crates/ra_hir_expand/src/builtin_derive.rs @@ -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 }) } }; } diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index e0fef613db01..d8b3d342ce3a 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -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(); diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index 0474523063cb..4c12d0a15962 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs @@ -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, diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs index 0b41d0e95810..6b482a60c540 100644 --- a/crates/ra_hir_expand/src/hygiene.rs +++ b/crates/ra_hir_expand/src/hygiene.rs @@ -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, + + // 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 { + if self.local_inner { + self.def_crate + } else { + None + } + } } diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 754a0f005bae..f440c073ba8a 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -204,6 +204,8 @@ pub struct MacroDefId { pub krate: Option, pub ast_id: Option>, pub kind: MacroDefKind, + + pub local_inner: bool, } impl MacroDefId { diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 04d3cd6a2795..65db6d1b0888 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml @@ -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" diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index a8ef32ec5973..a6f56c661e2a 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -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}; diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index a6f893037f5d..9ad6dbe075a2 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -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, 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( + db: &dyn HirDatabase, + res: TypeNs, + mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option, +) -> Option { + 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, diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 5ddecbdc6808..29e38a06c466 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -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!( diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 56abc65b8c65..3d3088965dec 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -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 + "### + ) +} diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 814354ffa6ab..05f825c6fe22 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -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!( diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index dd10f74e6d9e..aa56a5cd8703 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs @@ -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 `::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::<|> + } + " + ), + @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); + impl Super for Wrap {} + impl Sub for Wrap { + 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!( diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index f559f2b9705f..a6a5568de0bf 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs @@ -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: "()", }, ] diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 118fceb2e746..c529752d42d9 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -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()), diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 77d3543760bb..2edb130cf7bf 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -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, + ) { + 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, + path: Option, ) { 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); diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index b5e2785fe042..db3907fe64e1 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -26,6 +26,8 @@ pub struct FunctionSignature { pub kind: CallableKind, /// Optional visibility pub visibility: Option, + /// Qualifiers like `async`, `unsafe`, ... + pub qualifier: FunctionQualifier, /// Name of the function pub name: Option, /// 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, +} + impl FunctionSignature { pub(crate) fn with_doc_opt(mut self, doc: Option) -> 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)?, diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs index 4b081bf6c606..d37260e9647a 100644 --- a/crates/ra_ide/src/display/short_label.rs +++ b/crates/ra_ide/src/display/short_label.rs @@ -33,7 +33,11 @@ impl ShortLabel for ast::EnumDef { impl ShortLabel for ast::TraitDef { fn short_label(&self) -> Option { - 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 ") + } } } diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs index 4379005aa645..8657377dedaa 100644 --- a/crates/ra_ide/src/folding_ranges.rs +++ b/crates/ra_ide/src/folding_ranges.rs @@ -88,7 +88,7 @@ fn fold_kind(kind: SyntaxKind) -> Option { | ITEM_LIST | EXTERN_ITEM_LIST | USE_TREE_LIST - | BLOCK + | BLOCK_EXPR | MATCH_ARM_LIST | ENUM_VARIANT_LIST | TOKEN_TREE => Some(FoldKind::Block), diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 58c799eca7bd..54d318858227 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -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"], + ); + } } diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index fde0bfa98b34..63fd6b3e4571 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs @@ -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 + } +} + ", + ); + } } diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs index bf97f8c569c8..86c70ff830b8 100644 --- a/crates/ra_ide/src/syntax_tree.rs +++ b/crates/ra_ide/src/syntax_tree.rs @@ -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() ); diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index 0d924ce582fd..c43003fd63df 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs @@ -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 "}""# ); } diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index c2a6e82e9b45..be0cd5661bd6 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs @@ -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, ) -> Option { 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, diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs index cb30b25a890e..34f0397686fb 100644 --- a/crates/ra_parser/src/grammar/expressions.rs +++ b/crates/ra_parser/src/grammar/expressions.rs @@ -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); diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs index 0d277a58662d..706a2f796b21 100644 --- a/crates/ra_parser/src/grammar/expressions/atom.rs +++ b/crates/ra_parser/src/grammar/expressions/atom.rs @@ -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) -> 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) -> 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) -> 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) -> 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) -> 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 diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 433ed6812fb7..67a924de5397 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -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); diff --git a/crates/ra_parser/src/grammar/items/use_item.rs b/crates/ra_parser/src/grammar/items/use_item.rs index e3b991c8c6c9..3a0c7a31a852 100644 --- a/crates/ra_parser/src/grammar/items/use_item.rs +++ b/crates/ra_parser/src/grammar/items/use_item.rs @@ -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); } diff --git a/crates/ra_parser/src/grammar/type_args.rs b/crates/ra_parser/src/grammar/type_args.rs index 33d9973e9920..2d61f9d8083a 100644 --- a/crates/ra_parser/src/grammar/type_args.rs +++ b/crates/ra_parser/src/grammar/type_args.rs @@ -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() => { diff --git a/crates/ra_parser/src/lib.rs b/crates/ra_parser/src/lib.rs index 652492c1ed96..e08ad4dae673 100644 --- a/crates/ra_parser/src/lib.rs +++ b/crates/ra_parser/src/lib.rs @@ -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, diff --git a/crates/ra_parser/src/syntax_kind/generated.rs b/crates/ra_parser/src/syntax_kind/generated.rs index 524e7d784e5b..e7404492a8e9 100644 --- a/crates/ra_parser/src/syntax_kind/generated.rs +++ b/crates/ra_parser/src/syntax_kind/generated.rs @@ -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, diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml index ac2d156dc292..886e148703d7 100644 --- a/crates/ra_proc_macro_srv/Cargo.toml +++ b/crates/ra_proc_macro_srv/Cargo.toml @@ -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" diff --git a/crates/ra_proc_macro_srv/src/cli.rs b/crates/ra_proc_macro_srv/src/cli.rs index 7282e5b9b7a9..1437794c9e23 100644 --- a/crates/ra_proc_macro_srv/src/cli.rs +++ b/crates/ra_proc_macro_srv/src/cli.rs @@ -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) } }; diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs index d202eb0fde42..aa84e951cd56 100644 --- a/crates/ra_proc_macro_srv/src/dylib.rs +++ b/crates/ra_proc_macro_srv/src/dylib.rs @@ -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, + inner: ProcMacroLibraryLibloading, } impl Expander { - pub fn new(lib: &Path) -> Result { + pub fn new(lib: &Path) -> io::Result { // 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 { + 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 { + Ok(path.to_path_buf()) +} diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs index 3aca859db34b..922bb84bbfcd 100644 --- a/crates/ra_proc_macro_srv/src/lib.rs +++ b/crates/ra_proc_macro_srv/src/lib.rs @@ -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 { - 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 { + 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 { + 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; diff --git a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt index 1f5d940fa0bc..6776f5231743 100644 --- a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt +++ b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt @@ -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 \ No newline at end of file + PUNCH ; [alone] 4294967295 diff --git a/crates/ra_proc_macro_srv/src/tests/mod.rs b/crates/ra_proc_macro_srv/src/tests/mod.rs index 03f79bc5d60f..9cf58511ce7f 100644 --- a/crates/ra_proc_macro_srv/src/tests/mod.rs +++ b/crates/ra_proc_macro_srv/src/tests/mod.rs @@ -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, diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs index 2139ec7a4dac..646a427c5653 100644 --- a/crates/ra_proc_macro_srv/src/tests/utils.rs +++ b/crates/ra_proc_macro_srv/src/tests/utils.rs @@ -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 { 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() } diff --git a/crates/ra_prof/src/hprof.rs b/crates/ra_prof/src/hprof.rs index 2b8a903636ca..a3f5321fb3c1 100644 --- a/crates/ra_prof/src/hprof.rs +++ b/crates/ra_prof/src/hprof.rs @@ -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