]> git.lizzy.rs Git - rust.git/commitdiff
Merge #7353
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>
Fri, 22 Jan 2021 15:31:47 +0000 (15:31 +0000)
committerGitHub <noreply@github.com>
Fri, 22 Jan 2021 15:31:47 +0000 (15:31 +0000)
7353: Add LifetimeParam and ConstParam to CompletionItemKind r=matklad a=Veykril

Adds `LifetimeParam` and `ConstParam` to `CompletionItemKind` and maps them both to `TypeParam` in the protocol conversion as there are no equivalents, so nothing really changes there.
`ConstParam` could be mapped to `Const` I guess but I'm split on whether that would be better?

Additions were solely inspired by (the single) test output for const params.

Also sorts the variants of `CompletionItemKind` and its to_proto match.

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
89 files changed:
.github/workflows/release.yaml
Cargo.lock
crates/assists/src/handlers/generate_function.rs
crates/assists/src/handlers/generate_impl.rs
crates/assists/src/handlers/generate_new.rs
crates/assists/src/handlers/raw_string.rs
crates/assists/src/handlers/replace_derive_with_manual_impl.rs
crates/assists/src/utils.rs
crates/hir/src/attrs.rs
crates/hir/src/code_model.rs
crates/hir/src/db.rs
crates/hir_def/src/attr.rs
crates/hir_def/src/body.rs
crates/hir_def/src/body/tests.rs
crates/hir_def/src/db.rs
crates/hir_def/src/find_path.rs
crates/hir_def/src/import_map.rs
crates/hir_def/src/item_tree.rs
crates/hir_def/src/item_tree/lower.rs
crates/hir_def/src/lang_item.rs
crates/hir_def/src/nameres.rs
crates/hir_def/src/nameres/collector.rs
crates/hir_def/src/nameres/path_resolution.rs
crates/hir_def/src/nameres/tests.rs
crates/hir_def/src/nameres/tests/block.rs [new file with mode: 0644]
crates/hir_def/src/resolver.rs
crates/hir_def/src/test_db.rs
crates/hir_expand/src/ast_id_map.rs
crates/hir_expand/src/builtin_derive.rs
crates/hir_expand/src/db.rs
crates/hir_expand/src/name.rs
crates/hir_ty/Cargo.toml
crates/hir_ty/src/diagnostics.rs
crates/hir_ty/src/infer/expr.rs
crates/hir_ty/src/method_resolution.rs
crates/hir_ty/src/test_db.rs
crates/hir_ty/src/tests/simple.rs
crates/ide/src/display/navigation_target.rs
crates/ide/src/display/short_label.rs
crates/ide/src/doc_links.rs
crates/ide/src/extend_selection.rs
crates/ide/src/goto_definition.rs
crates/ide/src/hover.rs
crates/ide/src/inlay_hints.rs
crates/ide/src/join_lines.rs
crates/ide/src/runnables.rs
crates/ide/src/status.rs
crates/ide/src/syntax_highlighting/format.rs
crates/ide/src/syntax_highlighting/inject.rs
crates/ide_db/src/apply_change.rs
crates/ide_db/src/defs.rs
crates/ide_db/src/helpers/insert_use.rs
crates/ide_db/src/search.rs
crates/ide_db/src/symbol_index.rs
crates/mbe/src/syntax_bridge.rs
crates/profile/Cargo.toml
crates/profile/src/hprof.rs
crates/profile/src/lib.rs
crates/project_model/src/build_data.rs [new file with mode: 0644]
crates/project_model/src/cargo_workspace.rs
crates/project_model/src/lib.rs
crates/project_model/src/workspace.rs
crates/rust-analyzer/Cargo.toml
crates/rust-analyzer/src/bin/args.rs
crates/rust-analyzer/src/cli/analysis_stats.rs
crates/rust-analyzer/src/handlers.rs
crates/ssr/src/matching.rs
crates/ssr/src/replacing.rs
crates/ssr/src/resolving.rs
crates/syntax/Cargo.toml
crates/syntax/src/algo.rs
crates/syntax/src/ast.rs
crates/syntax/src/ast/make.rs
crates/syntax/src/ast/node_ext.rs
crates/syntax/src/ast/token_ext.rs
crates/syntax/src/lib.rs
crates/syntax/src/parsing/reparsing.rs
crates/syntax/src/parsing/text_tree_sink.rs
crates/syntax/src/syntax_node.rs
crates/syntax/src/validation.rs
docs/dev/README.md
docs/dev/style.md
xtask/Cargo.toml
xtask/src/codegen/gen_assists_docs.rs
xtask/src/codegen/gen_diagnostic_docs.rs
xtask/src/codegen/gen_feature_docs.rs
xtask/src/lib.rs
xtask/src/main.rs
xtask/tests/tidy.rs

index a97ed24baedbdef9bc56b3d4cf25cad8f86c78fa..94b6aa7c4ba06271c483e664b06ad0aca13ac407 100644 (file)
@@ -94,6 +94,7 @@ jobs:
         toolchain: stable
         profile: minimal
         override: true
+        components: rust-src
 
     - name: Install Nodejs
       uses: actions/setup-node@v1
@@ -108,10 +109,12 @@ jobs:
       if: github.ref != 'refs/heads/release'
       run: cargo xtask dist --nightly --client 0.3.$GITHUB_RUN_NUMBER-nightly
 
-    - name: Nightly analysis-stats check
-      if: github.ref != 'refs/heads/release'
+    - name: Run analysis-stats on rust-analyzer
       run: target/${{ env.RA_TARGET }}/release/rust-analyzer analysis-stats .
 
+    - name: Run analysis-stats on rust std library
+      run: target/${{ env.RA_TARGET }}/release/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std
+
     - name: Upload artifacts
       uses: actions/upload-artifact@v1
       with:
index 901784bec0fd2e94dd6be2f7f47fb640075fa5a8..bf1b9e4175a2a495de94b1df31889a60a7301b0c 100644 (file)
@@ -77,15 +77,15 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 
 [[package]]
 name = "backtrace"
-version = "0.3.55"
+version = "0.3.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598"
+checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc"
 dependencies = [
  "addr2line",
  "cfg-if 1.0.0",
  "libc",
  "miniz_oxide",
- "object 0.22.0",
+ "object",
  "rustc-demangle",
 ]
 
@@ -175,9 +175,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chalk-derive"
-version = "0.47.0"
+version = "0.50.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f00f6342a387edc822002d36a381e117afcac9f744951ff75fbf4a218edea5c"
+checksum = "ac605cf409013573e971d7292d4bec6f5495b19d5f98fc9d8b1a12270c3888e0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -187,9 +187,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.47.0"
+version = "0.50.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c686e69913591ae753e5526e73cbee39db3d9b0a92cc9078ab780cabf1c70aa9"
+checksum = "fa1dbfb3c2c8b67edb5cd981f720550e43579090574f786145731f90c5d401ff"
 dependencies = [
  "bitflags",
  "chalk-derive",
@@ -198,9 +198,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-recursive"
-version = "0.47.0"
+version = "0.50.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "310fdcac0340dab4163b766baa8067266e3b909108d1ac1b5246c033bde63975"
+checksum = "0882e2a3ba66901717a64f8bb0655e809f800ac6abed05cb605e7a41d4bf8999"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -211,9 +211,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.47.0"
+version = "0.50.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3c3252116111c3548f1164ab8d98c67c49848b3bde10dd11b650fd023e91c72"
+checksum = "0d43cce07150eac39771ff4b198537cefef744734b2218a89c682295b54cd8d0"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -273,6 +273,17 @@ version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
 
+[[package]]
+name = "countme"
+version = "2.0.0-pre.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5716604cba7c02a846ecad3f4a3fd2d2b641faccc2a24a51efb21aff0d01f35"
+dependencies = [
+ "dashmap",
+ "once_cell",
+ "rustc-hash",
+]
+
 [[package]]
 name = "crc32fast"
 version = "1.2.1"
@@ -323,7 +334,7 @@ dependencies = [
  "const_fn",
  "crossbeam-utils 0.8.1",
  "lazy_static",
- "memoffset 0.6.1",
+ "memoffset",
  "scopeguard",
 ]
 
@@ -349,6 +360,16 @@ dependencies = [
  "lazy_static",
 ]
 
+[[package]]
+name = "dashmap"
+version = "4.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
+dependencies = [
+ "cfg-if 1.0.0",
+ "num_cpus",
+]
+
 [[package]]
 name = "dissimilar"
 version = "1.0.2"
@@ -397,13 +418,13 @@ dependencies = [
 
 [[package]]
 name = "filetime"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe"
+checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.2.4",
  "winapi 0.3.9",
 ]
 
@@ -940,15 +961,6 @@ dependencies = [
  "winapi 0.3.9",
 ]
 
-[[package]]
-name = "memoffset"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
-dependencies = [
- "autocfg",
-]
-
 [[package]]
 name = "memoffset"
 version = "0.6.1"
@@ -1080,12 +1092,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "object"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
-
 [[package]]
 name = "object"
 version = "0.23.0"
@@ -1124,7 +1130,7 @@ dependencies = [
  "cfg-if 1.0.0",
  "instant",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.1.57",
  "smallvec",
  "winapi 0.3.9",
 ]
@@ -1205,9 +1211,9 @@ dependencies = [
 
 [[package]]
 name = "pico-args"
-version = "0.3.4"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1"
+checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6"
 
 [[package]]
 name = "pin-project-lite"
@@ -1251,7 +1257,7 @@ dependencies = [
  "libloading",
  "mbe",
  "memmap",
- "object 0.23.0",
+ "object",
  "proc_macro_api",
  "proc_macro_test",
  "serde_derive",
@@ -1269,6 +1275,7 @@ name = "profile"
 version = "0.0.0"
 dependencies = [
  "cfg-if 1.0.0",
+ "countme",
  "jemalloc-ctl",
  "la-arena",
  "libc",
@@ -1357,6 +1364,15 @@ version = "0.1.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 
+[[package]]
+name = "redox_syscall"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "regex"
 version = "1.4.3"
@@ -1384,15 +1400,15 @@ checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
 
 [[package]]
 name = "rowan"
-version = "0.10.6"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a0734142c18710f7214dc21908e2f054e973b908dbb1a602a3e6691615aaaae"
+checksum = "24c2d78254049413f9d73495f883e7fa0b7a7d4b88468cd72a3bbbd0ad585cd1"
 dependencies = [
+ "countme",
  "hashbrown",
+ "memoffset",
  "rustc-hash",
- "smol_str",
  "text-size",
- "triomphe",
 ]
 
 [[package]]
@@ -1448,9 +1464,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-ap-rustc_lexer"
-version = "697.0.0"
+version = "700.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67adbe260a0a11910624d6d28c0304fcf7b063e666682111005c83b09f73429d"
+checksum = "5ed36784376b69c941d7aa36e960a52ac712e2663960357121a4d9f2cc58e225"
 dependencies = [
  "unicode-xid",
 ]
@@ -1544,18 +1560,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.119"
+version = "1.0.120"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3"
+checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.119"
+version = "1.0.120"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd"
+checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1638,12 +1654,6 @@ dependencies = [
  "text_edit",
 ]
 
-[[package]]
-name = "stable_deref_trait"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
-
 [[package]]
 name = "stdx"
 version = "0.0.0"
@@ -1871,17 +1881,6 @@ dependencies = [
  "tracing-subscriber",
 ]
 
-[[package]]
-name = "triomphe"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e9d872053cf9e5a833d8c1dd772cdc38ab66a908129d6f73c049c986161d07c"
-dependencies = [
- "memoffset 0.5.6",
- "serde",
- "stable_deref_trait",
-]
-
 [[package]]
 name = "tt"
 version = "0.0.0"
index 06ac85f670e0ec544b485dd487490a9c858d303c..1805c1dfdf71a566cef3c9e20b6019e351f99816 100644 (file)
@@ -158,11 +158,11 @@ fn render(self) -> FunctionTemplate {
                 it.text_range().end()
             }
             GeneratedFunctionTarget::InEmptyItemList(it) => {
-                let indent = IndentLevel::from_node(it.syntax());
+                let indent = IndentLevel::from_node(&it);
                 leading_ws = format!("\n{}", indent + 1);
                 fn_def = fn_def.indent(indent + 1);
                 trailing_ws = format!("\n{}", indent);
-                it.syntax().text_range().start() + TextSize::of('{')
+                it.text_range().start() + TextSize::of('{')
             }
         };
 
@@ -179,14 +179,14 @@ fn render(self) -> FunctionTemplate {
 
 enum GeneratedFunctionTarget {
     BehindItem(SyntaxNode),
-    InEmptyItemList(ast::ItemList),
+    InEmptyItemList(SyntaxNode),
 }
 
 impl GeneratedFunctionTarget {
     fn syntax(&self) -> &SyntaxNode {
         match self {
             GeneratedFunctionTarget::BehindItem(it) => it,
-            GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(),
+            GeneratedFunctionTarget::InEmptyItemList(it) => it,
         }
     }
 }
@@ -323,7 +323,16 @@ fn next_space_for_fn_in_module(
             if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) {
                 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
             } else {
-                GeneratedFunctionTarget::InEmptyItemList(it.item_list()?)
+                GeneratedFunctionTarget::InEmptyItemList(it.item_list()?.syntax().clone())
+            }
+        }
+        hir::ModuleSource::BlockExpr(it) => {
+            if let Some(last_item) =
+                it.statements().take_while(|stmt| matches!(stmt, ast::Stmt::Item(_))).last()
+            {
+                GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
+            } else {
+                GeneratedFunctionTarget::InEmptyItemList(it.syntax().clone())
             }
         }
     };
index 9af45192b66a11494754181989d2e515806c72bb..82747727294ef7e79d3835871b2f9ace960b3127 100644 (file)
@@ -1,6 +1,9 @@
 use itertools::Itertools;
 use stdx::format_to;
-use syntax::ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner};
+use syntax::{
+    ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner},
+    SmolStr,
+};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -49,16 +52,16 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()
                 format_to!(buf, "{}", type_params.syntax());
             }
             buf.push_str(" ");
-            buf.push_str(name.text().as_str());
+            buf.push_str(name.text());
             if let Some(type_params) = type_params {
                 let lifetime_params = type_params
                     .lifetime_params()
                     .filter_map(|it| it.lifetime())
-                    .map(|it| it.text().clone());
+                    .map(|it| SmolStr::from(it.text()));
                 let type_params = type_params
                     .type_params()
                     .filter_map(|it| it.name())
-                    .map(|it| it.text().clone());
+                    .map(|it| SmolStr::from(it.text()));
 
                 let generic_params = lifetime_params.chain(type_params).format(", ");
                 format_to!(buf, "<{}>", generic_params)
index 5c52b2bc83c3fdff00bf544034c69bda4eab7627..b7390855ad76dd6f119c261dda60febe6cbb842a 100644 (file)
@@ -3,7 +3,7 @@
 use stdx::format_to;
 use syntax::{
     ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner},
-    T,
+    SmolStr, T,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -95,14 +95,14 @@ fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String {
         format_to!(buf, "{}", type_params.syntax());
     }
     buf.push_str(" ");
-    buf.push_str(strukt.name().unwrap().text().as_str());
+    buf.push_str(strukt.name().unwrap().text());
     if let Some(type_params) = type_params {
         let lifetime_params = type_params
             .lifetime_params()
             .filter_map(|it| it.lifetime())
-            .map(|it| it.text().clone());
+            .map(|it| SmolStr::from(it.text()));
         let type_params =
-            type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
+            type_params.type_params().filter_map(|it| it.name()).map(|it| SmolStr::from(it.text()));
         format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", "))
     }
 
index be963f1626c9dbf91b60ae6cb6355c4445ec4eba..d952676071044e3e3ba30d6214643c53a7f4aa78 100644 (file)
@@ -138,7 +138,7 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
         return None;
     }
 
-    let text = token.text().as_str();
+    let text = token.text();
     if !text.starts_with("r#") && text.ends_with('#') {
         return None;
     }
index bd4c1c806b8cd7015d9d2e97d8b9bcbeca604ecd..6aa9d2f2c9b412cccfc9a6189306b19e878a56d3 100644 (file)
@@ -3,7 +3,7 @@
 use itertools::Itertools;
 use syntax::{
     ast::{self, make, AstNode},
-    Direction, SmolStr,
+    Direction,
     SyntaxKind::{IDENT, WHITESPACE},
     TextSize,
 };
@@ -43,17 +43,18 @@ pub(crate) fn replace_derive_with_manual_impl(
 ) -> Option<()> {
     let attr = ctx.find_node_at_offset::<ast::Attr>()?;
 
-    let attr_name = attr
+    let has_derive = attr
         .syntax()
         .descendants_with_tokens()
         .filter(|t| t.kind() == IDENT)
         .find_map(syntax::NodeOrToken::into_token)
-        .filter(|t| t.text() == "derive")?
-        .text()
-        .clone();
+        .filter(|t| t.text() == "derive")
+        .is_some();
+    if !has_derive {
+        return None;
+    }
 
-    let trait_token =
-        ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?;
+    let trait_token = ctx.token_at_offset().find(|t| t.kind() == IDENT && t.text() != "derive")?;
     let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
 
     let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?;
@@ -176,9 +177,9 @@ fn update_attribute(
         .syntax()
         .descendants_with_tokens()
         .filter(|t| t.kind() == IDENT)
-        .filter_map(|t| t.into_token().map(|t| t.text().clone()))
+        .filter_map(|t| t.into_token().map(|t| t.text().to_string()))
         .filter(|t| t != trait_name.text())
-        .collect::<Vec<SmolStr>>();
+        .collect::<Vec<_>>();
     let has_more_derives = !new_attr_input.is_empty();
 
     if has_more_derives {
index fc9f83bab4793f6aa400f4875ecdfca5eade5a51..44c35bafaea59673dae296cdbeeb6cc68cef610e 100644 (file)
@@ -223,7 +223,7 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
             let method = mce.name_ref()?;
             let arg_list = mce.arg_list()?;
 
-            let method = match method.text().as_str() {
+            let method = match method.text() {
                 "is_some" => "is_none",
                 "is_none" => "is_some",
                 "is_ok" => "is_err",
index 99fb65bac3f2b9325811a4b846b9fbda43077910..9e6a3e15596ddb670f52736d3f8066c2333f3687 100644 (file)
@@ -2,6 +2,7 @@
 use hir_def::{
     attr::{Attrs, Documentation},
     path::ModPath,
+    per_ns::PerNs,
     resolver::HasResolver,
     AttrDefId, GenericParamId, ModuleDefId,
 };
@@ -112,6 +113,11 @@ fn resolve_doc_path(
     let path = ast::Path::parse(link).ok()?;
     let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap();
     let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
+    if resolved == PerNs::none() {
+        if let Some(trait_id) = resolver.resolve_module_path_in_trait_items(db.upcast(), &modpath) {
+            return Some(ModuleDefId::TraitId(trait_id));
+        };
+    }
     let def = match ns {
         Some(Namespace::Types) => resolved.take_types()?,
         Some(Namespace::Values) => resolved.take_values()?,
index a4141e11198b7ecac83dbdcda2f11a254e51a858..aaa7013b66a676554fb254782e418031dd51c96f 100644 (file)
@@ -90,7 +90,7 @@ pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> {
     }
 
     pub fn root_module(self, db: &dyn HirDatabase) -> Module {
-        let module_id = db.crate_def_map(self.id).root;
+        let module_id = db.crate_def_map(self.id).root();
         Module::new(self, module_id)
     }
 
@@ -302,7 +302,7 @@ pub fn krate(self) -> Crate {
     /// in the module tree of any target in `Cargo.toml`.
     pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
         let def_map = db.crate_def_map(self.id.krate);
-        self.with_module_id(def_map.root)
+        self.with_module_id(def_map.root())
     }
 
     /// Iterates over all child modules.
@@ -1000,7 +1000,7 @@ impl MacroDef {
     /// early, in `hir_expand`, where modules simply do not exist yet.
     pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
         let krate = self.id.krate;
-        let module_id = db.crate_def_map(krate).root;
+        let module_id = db.crate_def_map(krate).root();
         Some(Module::new(Crate { id: krate }, module_id))
     }
 
index d5d4cf5b6569bd4d2e2c6b91666c15c69ec07eb0..d444f4bbb17b4a7601ae757d27c6f491a8e2fc45 100644 (file)
@@ -1,13 +1,13 @@
 //! FIXME: write short doc here
 
 pub use hir_def::db::{
-    AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery,
-    CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, ExprScopesQuery,
-    FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, InternConstQuery,
-    InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery,
-    InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery,
-    ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery, TraitDataQuery,
-    TypeAliasDataQuery, UnionDataQuery,
+    AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery,
+    CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery,
+    ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
+    InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
+    InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
+    InternUnionQuery, ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery,
+    TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
 };
 pub use hir_expand::db::{
     AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternEagerExpansionQuery,
index 1b09ff816e0af8c8a0ddbfccfdf807c6a05d6b3e..c72649c41e4d183f8456db60326ab27ae2f135a1 100644 (file)
@@ -207,6 +207,7 @@ pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
                         mod_data.definition_source(db).as_ref().map(|src| match src {
                             ModuleSource::SourceFile(file) => file as &dyn AttrsOwner,
                             ModuleSource::Module(module) => module as &dyn AttrsOwner,
+                            ModuleSource::BlockExpr(block) => block as &dyn AttrsOwner,
                         }),
                     ),
                 }
index 3b2dd0f6e4420cfec9f49d79da3e7935dcdb55bd..2c2c999dd0a7ca2b56b6235080b119ea77bdb156 100644 (file)
@@ -122,7 +122,7 @@ pub(crate) fn enter_expand<T: ast::AstNode>(
 
         let mut err = None;
         let call_id =
-            macro_call.as_call_id_with_errors(db, self.crate_def_map.krate, resolver, &mut |e| {
+            macro_call.as_call_id_with_errors(db, self.crate_def_map.krate(), resolver, &mut |e| {
                 err.get_or_insert(e);
             });
         let call_id = match call_id {
index de77d5fc9866583b42e6a590394c8beab41fae45..2e5d0a01e76ea323b073b14509f08f7b802aad8e 100644 (file)
@@ -6,18 +6,24 @@
 use super::*;
 
 fn lower(ra_fixture: &str) -> Arc<Body> {
-    let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture);
+    let db = crate::test_db::TestDB::with_files(ra_fixture);
 
     let krate = db.crate_graph().iter().next().unwrap();
     let def_map = db.crate_def_map(krate);
-    let module = def_map.modules_for_file(file_id).next().unwrap();
-    let module = &def_map[module];
-    let fn_def = match module.scope.declarations().next().unwrap() {
-        ModuleDefId::FunctionId(it) => it,
-        _ => panic!(),
-    };
+    let mut fn_def = None;
+    'outer: for (_, module) in def_map.modules() {
+        for decl in module.scope.declarations() {
+            match decl {
+                ModuleDefId::FunctionId(it) => {
+                    fn_def = Some(it);
+                    break 'outer;
+                }
+                _ => {}
+            }
+        }
+    }
 
-    db.body(fn_def.into())
+    db.body(fn_def.unwrap().into())
 }
 
 fn check_diagnostics(ra_fixture: &str) {
@@ -41,6 +47,25 @@ macro_rules! n_nuple {
     );
 }
 
+#[test]
+fn macro_resolve() {
+    // Regression test for a path resolution bug introduced with inner item handling.
+    lower(
+        r"
+macro_rules! vec {
+    () => { () };
+    ($elem:expr; $n:expr) => { () };
+    ($($x:expr),+ $(,)?) => { () };
+}
+mod m {
+    fn outer() {
+        let _ = vec![FileSet::default(); self.len()];
+    }
+}
+      ",
+    );
+}
+
 #[test]
 fn cfg_diagnostics() {
     check_diagnostics(
index 91c8d45cd118b161c3ae85476b4d4060c960a28f..a87c80b8a1c5f811c5f89d92d7469d5c9be5a5f1 100644 (file)
@@ -2,9 +2,9 @@
 use std::sync::Arc;
 
 use base_db::{salsa, CrateId, SourceDatabase, Upcast};
-use hir_expand::{db::AstDatabase, HirFileId};
+use hir_expand::{db::AstDatabase, AstId, HirFileId};
 use la_arena::ArenaMap;
-use syntax::SmolStr;
+use syntax::{ast, SmolStr};
 
 use crate::{
     adt::{EnumData, StructData},
@@ -55,6 +55,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
     #[salsa::invoke(DefMap::crate_def_map_query)]
     fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>;
 
+    #[salsa::invoke(DefMap::block_def_map_query)]
+    fn block_def_map(&self, krate: CrateId, block: AstId<ast::BlockExpr>) -> Arc<DefMap>;
+
     #[salsa::invoke(StructData::struct_data_query)]
     fn struct_data(&self, id: StructId) -> Arc<StructData>;
     #[salsa::invoke(StructData::union_data_query)]
index 422a6eeb4a87d63e6b4ef97b4e4626cc775b1717..db2d125aeac3cf35497dec13707d93e6496ababa 100644 (file)
@@ -51,7 +51,7 @@ fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option<
     if item == ItemInNs::Types(from.into()) {
         // - if the item is the module we're in, use `self`
         Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
-    } else if let Some(parent_id) = def_map.modules[from.local_id].parent {
+    } else if let Some(parent_id) = def_map[from.local_id].parent {
         // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
         if item
             == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
@@ -111,7 +111,7 @@ fn find_path_inner(
 
     // - if the item is already in scope, return the name under which it is
     let def_map = db.crate_def_map(from.krate);
-    let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
+    let from_scope: &crate::item_scope::ItemScope = &def_map[from.local_id].scope;
     let scope_name =
         if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None };
     if prefixed.is_none() && scope_name.is_some() {
@@ -123,7 +123,7 @@ fn find_path_inner(
     if item
         == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
             krate: from.krate,
-            local_id: def_map.root,
+            local_id: def_map.root(),
         }))
     {
         return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
@@ -136,7 +136,7 @@ fn find_path_inner(
     }
 
     // - if the item is the crate root of a dependency crate, return the name from the extern prelude
-    for (name, def_id) in &def_map.extern_prelude {
+    for (name, def_id) in def_map.extern_prelude() {
         if item == ItemInNs::Types(*def_id) {
             let name = scope_name.unwrap_or_else(|| name.clone());
             return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
@@ -144,10 +144,10 @@ fn find_path_inner(
     }
 
     // - if the item is in the prelude, return the name from there
-    if let Some(prelude_module) = def_map.prelude {
+    if let Some(prelude_module) = def_map.prelude() {
         let prelude_def_map = db.crate_def_map(prelude_module.krate);
         let prelude_scope: &crate::item_scope::ItemScope =
-            &prelude_def_map.modules[prelude_module.local_id].scope;
+            &prelude_def_map[prelude_module.local_id].scope;
         if let Some((name, vis)) = prelude_scope.name_of(item) {
             if vis.is_visible_from(db, from) {
                 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()]));
@@ -175,7 +175,7 @@ fn find_path_inner(
 
     // - otherwise, look for modules containing (reexporting) it and import it from one of those
 
-    let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
+    let crate_root = ModuleId { local_id: def_map.root(), krate: from.krate };
     let crate_attrs = db.attrs(crate_root.into());
     let prefer_no_std = crate_attrs.by_key("no_std").exists();
     let mut best_path = None;
@@ -287,7 +287,7 @@ fn find_local_import_locations(
 
     // Compute the initial worklist. We start with all direct child modules of `from` as well as all
     // of its (recursive) parent modules.
-    let data = &def_map.modules[from.local_id];
+    let data = &def_map[from.local_id];
     let mut worklist = data
         .children
         .values()
@@ -296,7 +296,7 @@ fn find_local_import_locations(
     let mut parent = data.parent;
     while let Some(p) = parent {
         worklist.push(ModuleId { krate: from.krate, local_id: p });
-        parent = def_map.modules[p].parent;
+        parent = def_map[p].parent;
     }
 
     let mut seen: FxHashSet<_> = FxHashSet::default();
index fac0de90cd67ad604738a8334abee64f39a5b569..0251d016b74af757c833bde67babd5d5d8e068bb 100644 (file)
@@ -75,7 +75,7 @@ pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
 
         // We look only into modules that are public(ly reexported), starting with the crate root.
         let empty = ImportPath { segments: vec![] };
-        let root = ModuleId { krate, local_id: def_map.root };
+        let root = ModuleId { krate, local_id: def_map.root() };
         let mut worklist = vec![(root, empty)];
         while let Some((module, mod_path)) = worklist.pop() {
             let ext_def_map;
index ff62928dfb3aae0bf170687db69092bdb7ce4def..b8d7608e7cc535bbd4257dcc9fbf8a9da00e9ce7 100644 (file)
@@ -21,6 +21,7 @@
     HirFileId, InFile,
 };
 use la_arena::{Arena, Idx, RawIdx};
+use profile::Count;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
 use syntax::{ast, match_ast};
@@ -67,15 +68,16 @@ impl GenericParamsId {
 /// The item tree of a source file.
 #[derive(Debug, Eq, PartialEq)]
 pub struct ItemTree {
+    _c: Count<Self>,
+
     top_level: SmallVec<[ModItem; 1]>,
     attrs: FxHashMap<AttrOwner, RawAttrs>,
-    inner_items: FxHashMap<FileAstId<ast::Item>, SmallVec<[ModItem; 1]>>,
 
     data: Option<Box<ItemTreeData>>,
 }
 
 impl ItemTree {
-    pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
+    pub(crate) fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
         let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id));
         let syntax = if let Some(node) = db.parse_or_expand(file_id) {
             node
@@ -118,9 +120,9 @@ pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree
 
     fn empty() -> Self {
         Self {
+            _c: Count::new(),
             top_level: Default::default(),
             attrs: Default::default(),
-            inner_items: Default::default(),
             data: Default::default(),
         }
     }
@@ -147,6 +149,7 @@ fn shrink_to_fit(&mut self) {
                 macro_defs,
                 vis,
                 generics,
+                inner_items,
             } = &mut **data;
 
             imports.shrink_to_fit();
@@ -169,6 +172,8 @@ fn shrink_to_fit(&mut self) {
 
             vis.arena.shrink_to_fit();
             generics.arena.shrink_to_fit();
+
+            inner_items.shrink_to_fit();
         }
     }
 
@@ -191,16 +196,18 @@ pub fn attrs(&self, db: &dyn DefDatabase, krate: CrateId, of: AttrOwner) -> Attr
         self.raw_attrs(of).clone().filter(db, krate)
     }
 
-    /// Returns the lowered inner items that `ast` corresponds to.
-    ///
-    /// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered
-    /// to multiple items in the `ItemTree`.
-    pub fn inner_items(&self, ast: FileAstId<ast::Item>) -> &[ModItem] {
-        &self.inner_items[&ast]
+    pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
+        match &self.data {
+            Some(data) => Some(data.inner_items.values().flatten().copied()).into_iter().flatten(),
+            None => None.into_iter().flatten(),
+        }
     }
 
-    pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
-        self.inner_items.values().flatten().copied()
+    pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] {
+        match &self.data {
+            Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]),
+            None => &[],
+        }
     }
 
     pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source {
@@ -297,6 +304,8 @@ struct ItemTreeData {
 
     vis: ItemVisibilities,
     generics: GenericParamsStorage,
+
+    inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
 }
 
 #[derive(Debug, Eq, PartialEq, Hash)]
index 5e71ca42c471f64ea56b5f52ed20c2b4377f767f..ce470fc3bfe9e80bc7fd73e7f83f5a9620ca3486 100644 (file)
@@ -6,7 +6,7 @@
 use smallvec::SmallVec;
 use syntax::{
     ast::{self, ModuleItemOwner},
-    SyntaxNode,
+    SyntaxNode, WalkEvent,
 };
 
 use crate::{
@@ -37,7 +37,6 @@ pub(super) struct Ctx {
     file: HirFileId,
     source_ast_id_map: Arc<AstIdMap>,
     body_ctx: crate::body::LowerCtx,
-    inner_items: Vec<ModItem>,
     forced_visibility: Option<RawVisibilityId>,
 }
 
@@ -49,7 +48,6 @@ pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Se
             file,
             source_ast_id_map: db.ast_id_map(file),
             body_ctx: crate::body::LowerCtx::new(db, file),
-            inner_items: Vec::new(),
             forced_visibility: None,
         }
     }
@@ -73,8 +71,6 @@ fn data(&mut self) -> &mut ItemTreeData {
     }
 
     fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItems> {
-        assert!(inner || self.inner_items.is_empty());
-
         // Collect inner items for 1-to-1-lowered items.
         match item {
             ast::Item::Struct(_)
@@ -150,14 +146,37 @@ fn add_attrs(&mut self, item: AttrOwner, attrs: RawAttrs) {
 
     fn collect_inner_items(&mut self, container: &SyntaxNode) {
         let forced_vis = self.forced_visibility.take();
-        let mut inner_items = mem::take(&mut self.tree.inner_items);
-        inner_items.extend(container.descendants().skip(1).filter_map(ast::Item::cast).filter_map(
-            |item| {
-                let ast_id = self.source_ast_id_map.ast_id(&item);
-                Some((ast_id, self.lower_mod_item(&item, true)?.0))
-            },
-        ));
-        self.tree.inner_items = inner_items;
+
+        let mut block_stack = Vec::new();
+        for event in container.preorder().skip(1) {
+            match event {
+                WalkEvent::Enter(node) => {
+                    match_ast! {
+                        match node {
+                            ast::BlockExpr(block) => {
+                                block_stack.push(self.source_ast_id_map.ast_id(&block));
+                            },
+                            ast::Item(item) => {
+                                let mod_items = self.lower_mod_item(&item, true);
+                                let current_block = block_stack.last();
+                                if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
+                                    if !mod_items.0.is_empty() {
+                                        self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied());
+                                    }
+                                }
+                            },
+                            _ => {}
+                        }
+                    }
+                }
+                WalkEvent::Leave(node) => {
+                    if ast::BlockExpr::cast(node).is_some() {
+                        block_stack.pop();
+                    }
+                }
+            }
+        }
+
         self.forced_visibility = forced_vis;
     }
 
index 30188b740d3cb90dea3bddf02763a4d69d0a90a0..9e90f745ce285b6455e1801e8d93e7b056d28af7 100644 (file)
@@ -84,7 +84,7 @@ pub(crate) fn crate_lang_items_query(db: &dyn DefDatabase, krate: CrateId) -> Ar
 
         let crate_def_map = db.crate_def_map(krate);
 
-        for (_, module_data) in crate_def_map.modules.iter() {
+        for (_, module_data) in crate_def_map.modules() {
             for impl_def in module_data.scope.impls() {
                 lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDefId)
             }
index 769a557ad7893d49ac6f45f4b88d9eebe7c6f6e7..bd3ea9b8b1bf59e322198d04b53cda300d67ca4d 100644 (file)
 use base_db::{CrateId, Edition, FileId};
 use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile};
 use la_arena::Arena;
+use profile::Count;
 use rustc_hash::FxHashMap;
 use stdx::format_to;
-use syntax::ast;
+use syntax::{ast, AstNode};
 
 use crate::{
     db::DefDatabase,
 /// Contains all top-level defs from a macro-expanded crate
 #[derive(Debug, PartialEq, Eq)]
 pub struct DefMap {
-    pub root: LocalModuleId,
-    pub modules: Arena<ModuleData>,
-    pub(crate) krate: CrateId,
+    _c: Count<Self>,
+    parent: Option<Arc<DefMap>>,
+    root: LocalModuleId,
+    modules: Arena<ModuleData>,
+    krate: CrateId,
     /// The prelude module for this crate. This either comes from an import
     /// marked with the `prelude_import` attribute, or (in the normal case) from
     /// a dependency (`std` or `core`).
-    pub(crate) prelude: Option<ModuleId>,
-    pub(crate) extern_prelude: FxHashMap<Name, ModuleDefId>,
+    prelude: Option<ModuleId>,
+    extern_prelude: FxHashMap<Name, ModuleDefId>,
 
     edition: Edition,
     diagnostics: Vec<DefDiagnostic>,
@@ -109,6 +112,10 @@ pub enum ModuleOrigin {
     Inline {
         definition: AstId<ast::Module>,
     },
+    /// Pseudo-module introduced by a block scope (contains only inner items).
+    BlockExpr {
+        block: AstId<ast::BlockExpr>,
+    },
 }
 
 impl Default for ModuleOrigin {
@@ -122,7 +129,7 @@ fn declaration(&self) -> Option<AstId<ast::Module>> {
         match self {
             ModuleOrigin::File { declaration: module, .. }
             | ModuleOrigin::Inline { definition: module, .. } => Some(*module),
-            ModuleOrigin::CrateRoot { .. } => None,
+            ModuleOrigin::CrateRoot { .. } | ModuleOrigin::BlockExpr { .. } => None,
         }
     }
 
@@ -137,7 +144,7 @@ pub fn file_id(&self) -> Option<FileId> {
 
     pub fn is_inline(&self) -> bool {
         match self {
-            ModuleOrigin::Inline { .. } => true,
+            ModuleOrigin::Inline { .. } | ModuleOrigin::BlockExpr { .. } => true,
             ModuleOrigin::CrateRoot { .. } | ModuleOrigin::File { .. } => false,
         }
     }
@@ -155,6 +162,9 @@ fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> {
                 definition.file_id,
                 ModuleSource::Module(definition.to_node(db.upcast())),
             ),
+            ModuleOrigin::BlockExpr { block } => {
+                InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db.upcast())))
+            }
         }
     }
 }
@@ -174,24 +184,51 @@ pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<D
         let _p = profile::span("crate_def_map_query").detail(|| {
             db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
         });
-        let def_map = {
-            let edition = db.crate_graph()[krate].edition;
-            let mut modules: Arena<ModuleData> = Arena::default();
-            let root = modules.alloc(ModuleData::default());
-            DefMap {
-                krate,
-                edition,
-                extern_prelude: FxHashMap::default(),
-                prelude: None,
-                root,
-                modules,
-                diagnostics: Vec::new(),
-            }
-        };
-        let def_map = collector::collect_defs(db, def_map);
+        let edition = db.crate_graph()[krate].edition;
+        let def_map = DefMap::empty(krate, edition);
+        let def_map = collector::collect_defs(db, def_map, None);
         Arc::new(def_map)
     }
 
+    pub(crate) fn block_def_map_query(
+        db: &dyn DefDatabase,
+        krate: CrateId,
+        block: AstId<ast::BlockExpr>,
+    ) -> Arc<DefMap> {
+        let item_tree = db.item_tree(block.file_id);
+        let block_items = item_tree.inner_items_of_block(block.value);
+
+        let parent = parent_def_map(db, krate, block);
+
+        if block_items.is_empty() {
+            // If there are no inner items, nothing new is brought into scope, so we can just return
+            // the parent DefMap. This keeps DefMap parent chains short.
+            return parent;
+        }
+
+        let mut def_map = DefMap::empty(krate, parent.edition);
+        def_map.parent = Some(parent);
+
+        let def_map = collector::collect_defs(db, def_map, Some(block.value));
+        Arc::new(def_map)
+    }
+
+    fn empty(krate: CrateId, edition: Edition) -> DefMap {
+        let mut modules: Arena<ModuleData> = Arena::default();
+        let root = modules.alloc(ModuleData::default());
+        DefMap {
+            _c: Count::new(),
+            parent: None,
+            krate,
+            edition,
+            extern_prelude: FxHashMap::default(),
+            prelude: None,
+            root,
+            modules,
+            diagnostics: Vec::new(),
+        }
+    }
+
     pub fn add_diagnostics(
         &self,
         db: &dyn DefDatabase,
@@ -208,6 +245,26 @@ pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalMod
             .map(|(id, _data)| id)
     }
 
+    pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ {
+        self.modules.iter()
+    }
+
+    pub fn root(&self) -> LocalModuleId {
+        self.root
+    }
+
+    pub(crate) fn krate(&self) -> CrateId {
+        self.krate
+    }
+
+    pub(crate) fn prelude(&self) -> Option<ModuleId> {
+        self.prelude
+    }
+
+    pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleDefId)> + '_ {
+        self.extern_prelude.iter()
+    }
+
     pub(crate) fn resolve_path(
         &self,
         db: &dyn DefDatabase,
@@ -224,7 +281,12 @@ pub(crate) fn resolve_path(
     // even), as this should be a great debugging aid.
     pub fn dump(&self) -> String {
         let mut buf = String::new();
-        go(&mut buf, self, "crate", self.root);
+        let mut current_map = self;
+        while let Some(parent) = &current_map.parent {
+            go(&mut buf, current_map, "block scope", current_map.root);
+            current_map = &**parent;
+        }
+        go(&mut buf, current_map, "crate", current_map.root);
         return buf;
 
         fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
@@ -276,10 +338,40 @@ pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option<InFile<ast::Mod
     }
 }
 
+fn parent_def_map(
+    db: &dyn DefDatabase,
+    krate: CrateId,
+    block: AstId<ast::BlockExpr>,
+) -> Arc<DefMap> {
+    // FIXME: store this info in the item tree instead of reparsing here
+    let ast_id_map = db.ast_id_map(block.file_id);
+    let block_ptr = ast_id_map.get(block.value);
+    let root = match db.parse_or_expand(block.file_id) {
+        Some(it) => it,
+        None => {
+            return Arc::new(DefMap::empty(krate, Edition::Edition2018));
+        }
+    };
+    let ast = block_ptr.to_node(&root);
+
+    for ancestor in ast.syntax().ancestors().skip(1) {
+        if let Some(block_expr) = ast::BlockExpr::cast(ancestor) {
+            let ancestor_id = ast_id_map.ast_id(&block_expr);
+            let ast_id = InFile::new(block.file_id, ancestor_id);
+            let parent_map = db.block_def_map(krate, ast_id);
+            return parent_map;
+        }
+    }
+
+    // No enclosing block scope, so the parent is the crate-level DefMap.
+    db.crate_def_map(krate)
+}
+
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum ModuleSource {
     SourceFile(ast::SourceFile),
     Module(ast::Module),
+    BlockExpr(ast::BlockExpr),
 }
 
 mod diagnostics {
index 61da563404979694d2d96f598e9913ce68bca769..cd68efbe67872e66d9e5cff970daf41a00b19881 100644 (file)
 const EXPANSION_DEPTH_LIMIT: usize = 128;
 const FIXED_POINT_LIMIT: usize = 8192;
 
-pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap {
+pub(super) fn collect_defs(
+    db: &dyn DefDatabase,
+    mut def_map: DefMap,
+    block: Option<FileAstId<ast::BlockExpr>>,
+) -> DefMap {
     let crate_graph = db.crate_graph();
 
     // populate external prelude
@@ -93,6 +97,14 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap
         exports_proc_macros: false,
         from_glob_import: Default::default(),
     };
+    match block {
+        Some(block) => {
+            collector.seed_with_inner(block);
+        }
+        None => {
+            collector.seed_with_top_level();
+        }
+    }
     collector.collect();
     collector.finish()
 }
@@ -228,7 +240,7 @@ struct DefCollector<'a> {
 }
 
 impl DefCollector<'_> {
-    fn collect(&mut self) {
+    fn seed_with_top_level(&mut self) {
         let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
         let item_tree = self.db.item_tree(file_id.into());
         let module_id = self.def_map.root;
@@ -248,7 +260,31 @@ fn collect(&mut self) {
             }
             .collect(item_tree.top_level_items());
         }
+    }
+
+    fn seed_with_inner(&mut self, block: FileAstId<ast::BlockExpr>) {
+        let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
+        let item_tree = self.db.item_tree(file_id.into());
+        let module_id = self.def_map.root;
+        self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
+        if item_tree
+            .top_level_attrs(self.db, self.def_map.krate)
+            .cfg()
+            .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false))
+        {
+            ModCollector {
+                def_collector: &mut *self,
+                macro_depth: 0,
+                module_id,
+                file_id: file_id.into(),
+                item_tree: &item_tree,
+                mod_dir: ModDir::root(),
+            }
+            .collect(item_tree.inner_items_of_block(block));
+        }
+    }
 
+    fn collect(&mut self) {
         // main name resolution fixed-point loop.
         let mut i = 0;
         loop {
@@ -1470,7 +1506,6 @@ fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) {
 mod tests {
     use crate::{db::DefDatabase, test_db::TestDB};
     use base_db::{fixture::WithFixture, SourceDatabase};
-    use la_arena::Arena;
 
     use super::*;
 
@@ -1489,6 +1524,7 @@ fn do_collect_defs(db: &dyn DefDatabase, def_map: DefMap) -> DefMap {
             exports_proc_macros: false,
             from_glob_import: Default::default(),
         };
+        collector.seed_with_top_level();
         collector.collect();
         collector.def_map
     }
@@ -1497,20 +1533,8 @@ fn do_resolve(code: &str) -> DefMap {
         let (db, _file_id) = TestDB::with_single_file(&code);
         let krate = db.test_crate();
 
-        let def_map = {
-            let edition = db.crate_graph()[krate].edition;
-            let mut modules: Arena<ModuleData> = Arena::default();
-            let root = modules.alloc(ModuleData::default());
-            DefMap {
-                krate,
-                edition,
-                extern_prelude: FxHashMap::default(),
-                prelude: None,
-                root,
-                modules,
-                diagnostics: Vec::new(),
-            }
-        };
+        let edition = db.crate_graph()[krate].edition;
+        let def_map = DefMap::empty(krate, edition);
         do_collect_defs(&db, def_map)
     }
 
index 096a7d0ac114a6dfe6a6dc1cf935c59d6809667f..ec90f4e65054e96b250d6493943649cf0d0d8dd9 100644 (file)
@@ -103,6 +103,43 @@ pub(super) fn resolve_path_fp_with_macro(
         original_module: LocalModuleId,
         path: &ModPath,
         shadow: BuiltinShadowMode,
+    ) -> ResolvePathResult {
+        let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
+        result.segment_index = Some(usize::max_value());
+
+        let mut current_map = self;
+        loop {
+            let new = current_map.resolve_path_fp_with_macro_single(
+                db,
+                mode,
+                original_module,
+                path,
+                shadow,
+            );
+
+            // Merge `new` into `result`.
+            result.resolved_def = result.resolved_def.or(new.resolved_def);
+            if result.reached_fixedpoint == ReachedFixedPoint::No {
+                result.reached_fixedpoint = new.reached_fixedpoint;
+            }
+            // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates?
+            result.krate = result.krate.or(new.krate);
+            result.segment_index = result.segment_index.min(new.segment_index);
+
+            match &current_map.parent {
+                Some(map) => current_map = map,
+                None => return result,
+            }
+        }
+    }
+
+    pub(super) fn resolve_path_fp_with_macro_single(
+        &self,
+        db: &dyn DefDatabase,
+        mode: ResolveMode,
+        original_module: LocalModuleId,
+        path: &ModPath,
+        shadow: BuiltinShadowMode,
     ) -> ResolvePathResult {
         let mut segments = path.segments.iter().enumerate();
         let mut curr_per_ns: PerNs = match path.kind {
index 723481c367fc243caa9403f36f7c7f71289efcc7..73e3a4702bfded46da17b38df87c1e381ae0263f 100644 (file)
@@ -4,11 +4,13 @@
 mod mod_resolution;
 mod diagnostics;
 mod primitives;
+mod block;
 
 use std::sync::Arc;
 
 use base_db::{fixture::WithFixture, SourceDatabase};
 use expect_test::{expect, Expect};
+use hir_expand::db::AstDatabase;
 use test_utils::mark;
 
 use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
@@ -19,12 +21,30 @@ fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> {
     db.crate_def_map(krate)
 }
 
+fn compute_block_def_map(ra_fixture: &str) -> Arc<DefMap> {
+    let (db, position) = TestDB::with_position(ra_fixture);
+    let module = db.module_for_file(position.file_id);
+    let ast_map = db.ast_id_map(position.file_id.into());
+    let ast = db.parse(position.file_id);
+    let block: ast::BlockExpr =
+        syntax::algo::find_node_at_offset(&ast.syntax_node(), position.offset).unwrap();
+    let block_id = ast_map.ast_id(&block);
+
+    db.block_def_map(module.krate, InFile::new(position.file_id.into(), block_id))
+}
+
 fn check(ra_fixture: &str, expect: Expect) {
     let def_map = compute_crate_def_map(ra_fixture);
     let actual = def_map.dump();
     expect.assert_eq(&actual);
 }
 
+fn check_at(ra_fixture: &str, expect: Expect) {
+    let def_map = compute_block_def_map(ra_fixture);
+    let actual = def_map.dump();
+    expect.assert_eq(&actual);
+}
+
 #[test]
 fn crate_def_map_smoke_test() {
     check(
diff --git a/crates/hir_def/src/nameres/tests/block.rs b/crates/hir_def/src/nameres/tests/block.rs
new file mode 100644 (file)
index 0000000..01d6326
--- /dev/null
@@ -0,0 +1,97 @@
+use super::*;
+
+#[test]
+fn inner_item_smoke() {
+    check_at(
+        r#"
+struct inner {}
+fn outer() {
+    $0
+    fn inner() {}
+}
+"#,
+        expect![[r#"
+            block scope
+            inner: v
+            crate
+            inner: t
+            outer: v
+        "#]],
+    );
+}
+
+#[test]
+fn use_from_crate() {
+    check_at(
+        r#"
+struct Struct;
+fn outer() {
+    use Struct;
+    use crate::Struct as CrateStruct;
+    use self::Struct as SelfStruct;
+    $0
+}
+"#,
+        expect![[r#"
+            block scope
+            CrateStruct: t v
+            SelfStruct: t v
+            Struct: t v
+            crate
+            Struct: t v
+            outer: v
+        "#]],
+    );
+}
+
+#[test]
+fn merge_namespaces() {
+    check_at(
+        r#"
+struct name {}
+fn outer() {
+    fn name() {}
+
+    use name as imported; // should import both `name`s
+
+    $0
+}
+"#,
+        expect![[r#"
+            block scope
+            imported: t v
+            name: v
+            crate
+            name: t
+            outer: v
+        "#]],
+    );
+}
+
+#[test]
+fn nested_blocks() {
+    check_at(
+        r#"
+fn outer() {
+    struct inner1 {}
+    fn inner() {
+        use inner1;
+        use outer;
+        fn inner2() {}
+        $0
+    }
+}
+"#,
+        expect![[r#"
+            block scope
+            inner1: t
+            inner2: v
+            outer: v
+            block scope
+            inner: v
+            inner1: t
+            crate
+            outer: v
+        "#]],
+    );
+}
index a505bf2be8a47d9911037658eb9351744b363eac..b2f5776497daea599aa9f8ba57cb063d918c1c69 100644 (file)
@@ -146,6 +146,19 @@ pub fn resolve_module_path_in_items(&self, db: &dyn DefDatabase, path: &ModPath)
         self.resolve_module_path(db, path, BuiltinShadowMode::Module)
     }
 
+    pub fn resolve_module_path_in_trait_items(
+        &self,
+        db: &dyn DefDatabase,
+        path: &ModPath,
+    ) -> Option<TraitId> {
+        let (item_map, module) = self.module_scope()?;
+        let (module_res, ..) = item_map.resolve_path(db, module, &path, BuiltinShadowMode::Module);
+        match module_res.take_types()? {
+            ModuleDefId::TraitId(it) => Some(it),
+            _ => None,
+        }
+    }
+
     pub fn resolve_path_in_type_ns(
         &self,
         db: &dyn DefDatabase,
@@ -416,7 +429,7 @@ pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
         let mut traits = FxHashSet::default();
         for scope in &self.scopes {
             if let Scope::ModuleScope(m) = scope {
-                if let Some(prelude) = m.crate_def_map.prelude {
+                if let Some(prelude) = m.crate_def_map.prelude() {
                     let prelude_def_map = db.crate_def_map(prelude.krate);
                     traits.extend(prelude_def_map[prelude.local_id].scope.traits());
                 }
@@ -446,11 +459,11 @@ fn resolve_local_macro_def(&self, path: &ModPath) -> Option<MacroDefId> {
 
     pub fn module(&self) -> Option<ModuleId> {
         let (def_map, local_id) = self.module_scope()?;
-        Some(ModuleId { krate: def_map.krate, local_id })
+        Some(ModuleId { krate: def_map.krate(), local_id })
     }
 
     pub fn krate(&self) -> Option<CrateId> {
-        self.module_scope().map(|t| t.0.krate)
+        self.module_scope().map(|t| t.0.krate())
     }
 
     pub fn where_predicates_in_scope<'a>(
@@ -509,13 +522,13 @@ fn process_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef))
                     seen.insert((name.clone(), scope));
                     f(name.clone(), ScopeDef::PerNs(scope));
                 });
-                m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| {
+                m.crate_def_map.extern_prelude().for_each(|(name, &def)| {
                     f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public)));
                 });
                 BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
                     f(name.clone(), ScopeDef::PerNs(def));
                 });
-                if let Some(prelude) = m.crate_def_map.prelude {
+                if let Some(prelude) = m.crate_def_map.prelude() {
                     let prelude_def_map = db.crate_def_map(prelude.krate);
                     prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| {
                         let seen_tuple = (name.clone(), def);
index 574c0201a79ed073801883cc4980d04499636845..4ff219fb702f6d2566ad49115979fd6cb3d7b9b5 100644 (file)
@@ -75,7 +75,7 @@ impl TestDB {
     pub(crate) fn module_for_file(&self, file_id: FileId) -> crate::ModuleId {
         for &krate in self.relevant_crates(file_id).iter() {
             let crate_def_map = self.crate_def_map(krate);
-            for (local_id, data) in crate_def_map.modules.iter() {
+            for (local_id, data) in crate_def_map.modules() {
                 if data.origin.file_id() == Some(file_id) {
                     return crate::ModuleId { krate, local_id };
                 }
@@ -110,7 +110,7 @@ pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, St
         let crate_graph = self.crate_graph();
         for krate in crate_graph.iter() {
             let crate_def_map = self.crate_def_map(krate);
-            for (module_id, _) in crate_def_map.modules.iter() {
+            for (module_id, _) in crate_def_map.modules() {
                 let file_id = crate_def_map[module_id].origin.file_id();
                 files.extend(file_id)
             }
@@ -135,7 +135,7 @@ pub(crate) fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
             let crate_def_map = self.crate_def_map(krate);
 
             let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
-            for (module_id, module) in crate_def_map.modules.iter() {
+            for (module_id, module) in crate_def_map.modules() {
                 crate_def_map.add_diagnostics(self, module_id, &mut sink);
 
                 for decl in module.scope.declarations() {
index 2401b0cc59dd34871238d8521c7d290a6c71b45d..0991fffd8581f2f8dd29791ec5857371816dd5de 100644 (file)
@@ -13,7 +13,7 @@
 };
 
 use la_arena::{Arena, Idx};
-use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
+use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
 
 /// `AstId` points to an AST node in a specific file.
 pub struct FileAstId<N: AstNode> {
@@ -72,12 +72,20 @@ pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
         // get lower ids then children. That is, adding a new child does not
         // change parent's id. This means that, say, adding a new function to a
         // trait does not change ids of top-level items, which helps caching.
-        bdfs(node, |it| match ast::Item::cast(it) {
-            Some(module_item) => {
-                res.alloc(module_item.syntax());
-                true
+        bdfs(node, |it| {
+            match_ast! {
+                match it {
+                    ast::Item(module_item) => {
+                        res.alloc(module_item.syntax());
+                        true
+                    },
+                    ast::BlockExpr(block) => {
+                        res.alloc(block.syntax());
+                        true
+                    },
+                    _ => false,
+                }
             }
-            None => false,
         });
         res
     }
index eb257579f51633baaf08677418ab8dbde43be9cf..b7f1aae8fa98d4e22ab7e9d887498d289bd8e4f8 100644 (file)
@@ -102,7 +102,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
         debug!("name token not found");
         mbe::ExpandError::ConversionError
     })?;
-    let name_token = tt::Ident { id: name_token_id, text: name.text().clone() };
+    let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
     let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
     Ok(BasicAdtInfo { name: name_token, type_params })
 }
index 467516eb740b0caf10869b0275cd4d8591c66d4a..cb6e233209505514492ab8195cf54160ea9aa88b 100644 (file)
@@ -173,7 +173,7 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
     };
     let loc = db.lookup_intern_macro(id);
     let arg = loc.kind.arg(db)?;
-    Some(arg.green().clone())
+    Some(arg.green().to_owned())
 }
 
 fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
index 95d853b6da6fe333fb694582986a864b878c7c61..d692cec145a138c68c1d00cdf98ff8b2406b25fa 100644 (file)
@@ -38,7 +38,7 @@ pub fn new_tuple_field(idx: usize) -> Name {
     }
 
     pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
-        Self::new_text(lt.text().clone())
+        Self::new_text(lt.text().into())
     }
 
     /// Shortcut to create inline plain text name
@@ -47,12 +47,12 @@ const fn new_inline(text: &str) -> Name {
     }
 
     /// Resolve a name from the text of token.
-    fn resolve(raw_text: &SmolStr) -> Name {
+    fn resolve(raw_text: &str) -> Name {
         let raw_start = "r#";
-        if raw_text.as_str().starts_with(raw_start) {
+        if raw_text.starts_with(raw_start) {
             Name::new_text(SmolStr::new(&raw_text[raw_start.len()..]))
         } else {
-            Name::new_text(raw_text.clone())
+            Name::new_text(raw_text.into())
         }
     }
 
index 98434b741082f8d761911ba4f7f43699f88e464f..db42a00dc1224783fd751f0e99bed5d340783a80 100644 (file)
@@ -17,9 +17,9 @@ ena = "0.14.0"
 log = "0.4.8"
 rustc-hash = "1.1.0"
 scoped-tls = "1"
-chalk-solve = { version = "0.47", default-features = false }
-chalk-ir = "0.47"
-chalk-recursive = "0.47"
+chalk-solve = { version = "0.50", default-features = false }
+chalk-ir = "0.50"
+chalk-recursive = "0.50"
 la-arena = { version = "0.2.0", path = "../../lib/arena" }
 
 stdx = { path = "../stdx", version = "0.0.0" }
index c67a289f28328872c168eb088b9161a07fb677eb..247da43f22d13ae7a796068087793f2034a9145f 100644 (file)
@@ -409,7 +409,7 @@ fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
                 let crate_def_map = self.crate_def_map(krate);
 
                 let mut fns = Vec::new();
-                for (module_id, _) in crate_def_map.modules.iter() {
+                for (module_id, _) in crate_def_map.modules() {
                     for decl in crate_def_map[module_id].scope.declarations() {
                         let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
                         validate_module_item(self, krate, decl, &mut sink);
index 9bf3b51b0056e0e2ba4540570fab7fba69bf0299..d7351d21270193138c6d380e1f894648d4b9dfd3 100644 (file)
@@ -491,7 +491,10 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
             Expr::Box { expr } => {
                 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
                 if let Some(box_) = self.resolve_boxed_box() {
-                    Ty::apply_one(TypeCtor::Adt(box_), inner_ty)
+                    let mut sb = Substs::build_for_type_ctor(self.db, TypeCtor::Adt(box_));
+                    sb = sb.push(inner_ty);
+                    sb = sb.fill(repeat_with(|| self.table.new_type_var()));
+                    Ty::apply(TypeCtor::Adt(box_), sb.build())
                 } else {
                     Ty::Unknown
                 }
index 8a289f52a6514456a8a5ac9d63349f5337fd254f..f06aeeb4286eaebf62dce2641758fc836c94025f 100644 (file)
@@ -112,7 +112,7 @@ pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -
         let mut impls = Self { map: FxHashMap::default() };
 
         let crate_def_map = db.crate_def_map(krate);
-        for (_module_id, module_data) in crate_def_map.modules.iter() {
+        for (_module_id, module_data) in crate_def_map.modules() {
             for impl_id in module_data.scope.impls() {
                 let target_trait = match db.impl_trait(impl_id) {
                     Some(tr) => tr.value.trait_,
@@ -198,7 +198,7 @@ pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId
         let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default();
 
         let crate_def_map = db.crate_def_map(krate);
-        for (_module_id, module_data) in crate_def_map.modules.iter() {
+        for (_module_id, module_data) in crate_def_map.modules() {
             for impl_id in module_data.scope.impls() {
                 let data = db.impl_data(impl_id);
                 if data.target_trait.is_some() {
index 646e16bbecf5272ab8a221faa08630e6218f635c..3bbcbc242a8131b66765e062ae22e8dd384a84ec 100644 (file)
@@ -81,7 +81,7 @@ impl TestDB {
     pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId {
         for &krate in self.relevant_crates(file_id).iter() {
             let crate_def_map = self.crate_def_map(krate);
-            for (local_id, data) in crate_def_map.modules.iter() {
+            for (local_id, data) in crate_def_map.modules() {
                 if data.origin.file_id() == Some(file_id) {
                     return ModuleId { krate, local_id };
                 }
@@ -95,7 +95,7 @@ pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, St
         let crate_graph = self.crate_graph();
         for krate in crate_graph.iter() {
             let crate_def_map = self.crate_def_map(krate);
-            for (module_id, _) in crate_def_map.modules.iter() {
+            for (module_id, _) in crate_def_map.modules() {
                 let file_id = crate_def_map[module_id].origin.file_id();
                 files.extend(file_id)
             }
index 8d431b9201aa273424cba2799cf4b92627c42bf4..16682f76fce29276f8ae15195446cee3bb2952d9 100644 (file)
@@ -28,6 +28,30 @@ pub struct Box<T: ?Sized> {
     );
 }
 
+#[test]
+fn infer_box_with_allocator() {
+    check_types(
+        r#"
+//- /main.rs crate:main deps:std
+fn test() {
+    let x = box 1;
+    let t = (x, box x, box &1, box [1]);
+    t;
+} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; _], {unknown}>)
+
+//- /std.rs crate:std
+#[prelude_import] use prelude::*;
+mod boxed {
+    #[lang = "owned_box"]
+    pub struct Box<T: ?Sized, A: Allocator> {
+        inner: *mut T,
+        allocator: A,
+    }
+}
+"#,
+    );
+}
+
 #[test]
 fn infer_adt_self() {
     check_types(
index 8d08e4763ddb43db65c91c80045e0f012cb16c07..16fa828ad31b205a62f420d90db8bba4e0db57ff 100644 (file)
@@ -130,8 +130,7 @@ pub(crate) fn from_named(
         node: InFile<&dyn ast::NameOwner>,
         kind: SymbolKind,
     ) -> NavigationTarget {
-        let name =
-            node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
+        let name = node.value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
         let focus_range =
             node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range);
         let frange = node.map(|it| it.syntax()).original_file_range(db);
@@ -272,6 +271,7 @@ fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
             ModuleSource::Module(node) => {
                 (node.syntax(), node.name().map(|it| it.syntax().text_range()))
             }
+            ModuleSource::BlockExpr(node) => (node.syntax(), None),
         };
         let frange = src.with_value(syntax).original_file_range(db);
         NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module)
index 990f740b8b5e2d0f1d425872d4608381c56d579d..7ac050473f1bddf29b8531d0e7cb3d3863c8c5bc 100644 (file)
@@ -53,6 +53,12 @@ fn short_label(&self) -> Option<String> {
     }
 }
 
+impl ShortLabel for ast::BlockExpr {
+    fn short_label(&self) -> Option<String> {
+        None
+    }
+}
+
 impl ShortLabel for ast::TypeAlias {
     fn short_label(&self) -> Option<String> {
         short_label_from_node(self, "type ")
@@ -90,7 +96,7 @@ fn short_label(&self) -> Option<String> {
 impl ShortLabel for ast::ConstParam {
     fn short_label(&self) -> Option<String> {
         let mut buf = "const ".to_owned();
-        buf.push_str(self.name()?.text().as_str());
+        buf.push_str(self.name()?.text());
         if let Some(type_ref) = self.ty() {
             format_to!(buf, ": {}", type_ref.syntax());
         }
@@ -117,6 +123,6 @@ fn short_label_from_node<T>(node: &T, label: &str) -> Option<String>
 {
     let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default();
     buf.push_str(label);
-    buf.push_str(node.name()?.text().as_str());
+    buf.push_str(node.name()?.text());
     Some(buf)
 }
index 1f08d7810388c80517c3ecf52b33305422140f01..730e0dd0aea344338225273d2829d29356ce20ee 100644 (file)
@@ -221,14 +221,31 @@ fn rewrite_intra_doc_link(
     }?;
     let krate = resolved.module(db)?.krate();
     let canonical_path = resolved.canonical_path(db)?;
-    let new_target = get_doc_url(db, &krate)?
+    let mut new_url = get_doc_url(db, &krate)?
         .join(&format!("{}/", krate.display_name(db)?))
         .ok()?
         .join(&canonical_path.replace("::", "/"))
         .ok()?
         .join(&get_symbol_filename(db, &resolved)?)
-        .ok()?
-        .into_string();
+        .ok()?;
+
+    if let ModuleDef::Trait(t) = resolved {
+        let items = t.items(db);
+        if let Some(field_or_assoc_item) = items.iter().find_map(|assoc_item| {
+            if let Some(name) = assoc_item.name(db) {
+                if link.to_string() == format!("{}::{}", canonical_path, name) {
+                    return Some(FieldOrAssocItem::AssocItem(*assoc_item));
+                }
+            }
+            None
+        }) {
+            if let Some(fragment) = get_symbol_fragment(db, &field_or_assoc_item) {
+                new_url = new_url.join(&fragment).ok()?;
+            }
+        };
+    }
+
+    let new_target = new_url.into_string();
     let new_title = strip_prefixes_suffixes(title);
     Some((new_target, new_title.to_string()))
 }
index 17a540972e149ed37f5aa0a2688046981722fafc..2d722dee0e95b990161be2da4cb7f38f86576e79 100644 (file)
@@ -213,8 +213,8 @@ fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextSize) -> TextRange
     let ws_text = ws.text();
     let suffix = TextRange::new(offset, ws.text_range().end()) - ws.text_range().start();
     let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start();
-    let ws_suffix = &ws_text.as_str()[suffix];
-    let ws_prefix = &ws_text.as_str()[prefix];
+    let ws_suffix = &ws_text[suffix];
+    let ws_prefix = &ws_text[prefix];
     if ws_text.contains('\n') && !ws_suffix.contains('\n') {
         if let Some(node) = ws.next_sibling_or_token() {
             let start = match ws_prefix.rfind('\n') {
index a1d2bce1d17b113144ecaebb2a5c47cf2c9b543f..1a997fa40dc4d9e45de88067cf013665f18372c9 100644 (file)
@@ -2,16 +2,14 @@
 use hir::{HasAttrs, ModuleDef, Semantics};
 use ide_db::{
     defs::{Definition, NameClass, NameRefClass},
-    symbol_index, RootDatabase,
+    RootDatabase,
 };
 use syntax::{
     ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T,
 };
 
 use crate::{
-    display::{ToNav, TryToNav},
-    doc_links::extract_definitions_from_markdown,
-    runnables::doc_owner_to_def,
+    display::TryToNav, doc_links::extract_definitions_from_markdown, runnables::doc_owner_to_def,
     FilePosition, NavigationTarget, RangeInfo,
 };
 
@@ -38,28 +36,26 @@ pub(crate) fn goto_definition(
         return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
     }
 
-    let nav_targets = match_ast! {
+    let nav = match_ast! {
         match parent {
             ast::NameRef(name_ref) => {
-                reference_definition(&sema, Either::Right(&name_ref)).to_vec()
+                reference_definition(&sema, Either::Right(&name_ref))
             },
             ast::Name(name) => {
                 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
-                let nav = def.try_to_nav(sema.db)?;
-                vec![nav]
+                def.try_to_nav(sema.db)
             },
             ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
                 let def = name_class.referenced_or_defined(sema.db);
-                let nav = def.try_to_nav(sema.db)?;
-                vec![nav]
+                def.try_to_nav(sema.db)
             } else {
-                reference_definition(&sema, Either::Left(&lt)).to_vec()
+                reference_definition(&sema, Either::Left(&lt))
             },
             _ => return None,
         }
     };
 
-    Some(RangeInfo::new(original_token.text_range(), nav_targets))
+    Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
 }
 
 fn def_for_doc_comment(
@@ -120,42 +116,16 @@ fn priority(n: &SyntaxToken) -> usize {
     }
 }
 
-#[derive(Debug)]
-pub(crate) enum ReferenceResult {
-    Exact(NavigationTarget),
-    Approximate(Vec<NavigationTarget>),
-}
-
-impl ReferenceResult {
-    fn to_vec(self) -> Vec<NavigationTarget> {
-        match self {
-            ReferenceResult::Exact(target) => vec![target],
-            ReferenceResult::Approximate(vec) => vec,
-        }
-    }
-}
-
 pub(crate) fn reference_definition(
     sema: &Semantics<RootDatabase>,
     name_ref: Either<&ast::Lifetime, &ast::NameRef>,
-) -> ReferenceResult {
+) -> Option<NavigationTarget> {
     let name_kind = name_ref.either(
         |lifetime| NameRefClass::classify_lifetime(sema, lifetime),
         |name_ref| NameRefClass::classify(sema, name_ref),
-    );
-    if let Some(def) = name_kind {
-        let def = def.referenced(sema.db);
-        return match def.try_to_nav(sema.db) {
-            Some(nav) => ReferenceResult::Exact(nav),
-            None => ReferenceResult::Approximate(Vec::new()),
-        };
-    }
-
-    // Fallback index based approach:
-    let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text);
-    let navs =
-        symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect();
-    ReferenceResult::Approximate(navs)
+    )?;
+    let def = name_kind.referenced(sema.db);
+    def.try_to_nav(sema.db)
 }
 
 #[cfg(test)]
@@ -192,12 +162,12 @@ fn check(ra_fixture: &str) {
     fn goto_def_for_extern_crate() {
         check(
             r#"
-            //- /main.rs crate:main deps:std
-            extern crate std$0;
-            //- /std/lib.rs crate:std
-            // empty
-            //^ file
-            "#,
+//- /main.rs crate:main deps:std
+extern crate std$0;
+//- /std/lib.rs crate:std
+// empty
+//^ file
+"#,
         )
     }
 
@@ -205,12 +175,12 @@ fn goto_def_for_extern_crate() {
     fn goto_def_for_renamed_extern_crate() {
         check(
             r#"
-            //- /main.rs crate:main deps:std
-            extern crate std as abc$0;
-            //- /std/lib.rs crate:std
-            // empty
-            //^ file
-            "#,
+//- /main.rs crate:main deps:std
+extern crate std as abc$0;
+//- /std/lib.rs crate:std
+// empty
+//^ file
+"#,
         )
     }
 
@@ -297,13 +267,13 @@ fn bar() {
     fn goto_def_for_macros_from_other_crates() {
         check(
             r#"
-//- /lib.rs
+//- /lib.rs crate:main deps:foo
 use foo::foo;
 fn bar() {
     $0foo!();
 }
 
-//- /foo/lib.rs
+//- /foo/lib.rs crate:foo
 #[macro_export]
 macro_rules! foo { () => { () } }
            //^^^
@@ -315,10 +285,10 @@ macro_rules! foo { () => { () } }
     fn goto_def_for_macros_in_use_tree() {
         check(
             r#"
-//- /lib.rs
+//- /lib.rs crate:main deps:foo
 use foo::foo$0;
 
-//- /foo/lib.rs
+//- /foo/lib.rs crate:foo
 #[macro_export]
 macro_rules! foo { () => { () } }
            //^^^
@@ -976,10 +946,10 @@ fn goto_def_for_type_alias_generic_parameter() {
     fn goto_def_for_macro_container() {
         check(
             r#"
-//- /lib.rs
+//- /lib.rs crate:main deps:foo
 foo::module$0::mac!();
 
-//- /foo/lib.rs
+//- /foo/lib.rs crate:foo
 pub mod module {
       //^^^^^^
     #[macro_export]
index 44ebdbd35db3d4db30a91b977a2bf87c33672a48..d47a4cb0f1b98aecf87d646a886def52f984259a 100644 (file)
@@ -321,6 +321,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
                 match it.definition_source(db).value {
                     ModuleSource::Module(it) => it.short_label(),
                     ModuleSource::SourceFile(it) => it.short_label(),
+                    ModuleSource::BlockExpr(it) => it.short_label(),
                 },
                 mod_path,
             ),
@@ -1825,6 +1826,35 @@ pub struct Bar
             "#]],
         );
     }
+    #[test]
+    fn test_hover_intra_link_reference_to_trait_method() {
+        check(
+            r#"
+pub trait Foo {
+    fn buzz() -> usize;
+}
+/// [Foo][buzz]
+///
+/// [buzz]: Foo::buzz
+pub struct B$0ar
+"#,
+            expect![[r#"
+                *Bar*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub struct Bar
+                ```
+
+                ---
+
+                [Foo](https://docs.rs/test/*/test/trait.Foo.html#tymethod.buzz)
+            "#]],
+        );
+    }
 
     #[test]
     fn test_hover_external_url() {
index a2039fcc73cd6fd08b6dd1da737f3765475125d4..54485fd308c09e2f82a1039ee2ea68511296823d 100644 (file)
@@ -411,7 +411,7 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
     match expr {
         ast::Expr::MethodCallExpr(method_call_expr) => {
             let name_ref = method_call_expr.name_ref()?;
-            match name_ref.text().as_str() {
+            match name_ref.text() {
                 "clone" => method_call_expr.receiver().map(|rec| rec.to_string()),
                 name_ref => Some(name_ref.to_owned()),
             }
index 981467c8d3c7737399d16d68c05dfe6c84de7309..631bde0f19765233d25fdbba39dbc42e0c65d0a1 100644 (file)
@@ -59,7 +59,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
         // The node is either the first or the last in the file
         let suff = &token.text()[TextRange::new(
             offset - token.text_range().start() + TextSize::of('\n'),
-            TextSize::of(token.text().as_str()),
+            TextSize::of(token.text()),
         )];
         let spaces = suff.bytes().take_while(|&b| b == b' ').count();
 
index e282b31afbad7dda1ce0e2df9ebe921e3d514aa9..33170906d8670a7f4e15583651ca0d254464dc60 100644 (file)
@@ -9,6 +9,7 @@
     ast::{self, AstNode, AttrsOwner},
     match_ast, SyntaxNode,
 };
+use test_utils::mark;
 
 use crate::{
     display::{ToNav, TryToNav},
@@ -96,28 +97,26 @@ pub fn action(&self) -> &'static RunnableAction {
 pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
     let sema = Semantics::new(db);
     let module = match sema.to_module_def(file_id) {
-        None => return vec![],
+        None => return Vec::new(),
         Some(it) => it,
     };
 
-    runnables_mod(&sema, module)
+    let mut res = Vec::new();
+    runnables_mod(&sema, &mut res, module);
+    res
 }
 
-fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Runnable> {
-    let mut res: Vec<Runnable> = module
-        .declarations(sema.db)
-        .into_iter()
-        .filter_map(|def| {
-            let runnable = match def {
-                hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
-                hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
-                _ => None,
-            };
-            runnable.or_else(|| module_def_doctest(&sema, def))
-        })
-        .collect();
+fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) {
+    acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| {
+        let runnable = match def {
+            hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
+            hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
+            _ => None,
+        };
+        runnable.or_else(|| module_def_doctest(&sema, def))
+    }));
 
-    res.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map(
+    acc.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map(
         |def| match def {
             hir::AssocItem::Function(it) => {
                 runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
@@ -127,12 +126,15 @@ fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Run
         },
     ));
 
-    res.extend(module.declarations(sema.db).into_iter().flat_map(|def| match def {
-        hir::ModuleDef::Module(it) => runnables_mod(sema, it),
-        _ => vec![],
-    }));
-
-    res
+    for def in module.declarations(sema.db) {
+        if let hir::ModuleDef::Module(submodule) = def {
+            match submodule.definition_source(sema.db).value {
+                hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
+                hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules),
+                hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable
+            }
+        }
+    }
 }
 
 pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
@@ -326,6 +328,7 @@ fn has_test_function_or_multiple_test_submodules(
 #[cfg(test)]
 mod tests {
     use expect_test::{expect, Expect};
+    use test_utils::mark;
 
     use crate::fixture;
 
@@ -1050,4 +1053,25 @@ mod tests {
             "#]],
         );
     }
+
+    #[test]
+    fn dont_recurse_in_outline_submodules() {
+        mark::check!(dont_recurse_in_outline_submodules);
+        check(
+            r#"
+//- /lib.rs
+$0
+mod m;
+//- /m.rs
+mod tests {
+    #[test]
+    fn t() {}
+}
+"#,
+            &[],
+            expect![[r#"
+                []
+            "#]],
+        );
+    }
 }
index e10d7c3a454fd67e6aa57b10d778dfd091b067c2..137c38c0d9c1dcfcc0c7841d422ac6b5385ff2d5 100644 (file)
@@ -38,6 +38,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
     format_to!(buf, "{}\n", syntax_tree_stats(db));
     format_to!(buf, "{} (macros)\n", macro_syntax_tree_stats(db));
     format_to!(buf, "{} total\n", memory_usage());
+    format_to!(buf, "\ncounts:\n{}", profile::countme::get_all());
 
     if let Some(file_id) = file_id {
         format_to!(buf, "\nfile info:\n");
@@ -60,6 +61,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
             None => format_to!(buf, "does not belong to any crate"),
         }
     }
+
     buf
 }
 
index 63bff6376f787c9f82dcf739b2d1b735c0788ef9..8c67a0863c1d55b2c1f5653e2afbb5bea12a8bbe 100644 (file)
@@ -31,7 +31,7 @@ fn is_format_string(string: &ast::String) -> Option<()> {
     let parent = string.syntax().parent();
 
     let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?;
-    if !matches!(name.text().as_str(), "format_args" | "format_args_nl") {
+    if !matches!(name.text(), "format_args" | "format_args_nl") {
         return None;
     }
 
index 281461493532386dfadca675c5ba465ea8f991c0..8cdc3688f4efacbe927af5a5faf29025bdc20d7f 100644 (file)
@@ -116,7 +116,7 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
             None => (),
         }
 
-        let line: &str = comment.text().as_str();
+        let line: &str = comment.text();
         let range = comment.syntax().text_range();
 
         let mut pos = TextSize::of(comment.prefix());
index c770a236b0906f85e9012ffd282a61b52042c056..9d9b6de7a15b34e05dabe6b2166522d76b9de7c4 100644 (file)
@@ -149,6 +149,7 @@ macro_rules! sweep_each_query {
 
             // DefDatabase
             hir::db::ItemTreeQuery
+            hir::db::BlockDefMapQuery
             hir::db::CrateDefMapQueryQuery
             hir::db::StructDataQuery
             hir::db::UnionDataQuery
index d9875ffef6a9f46bd77a448049d20bb47ef0b974..a8091dbee83e175ef0de2b0e2d0266e887f654e4 100644 (file)
@@ -343,7 +343,7 @@ pub fn classify(
                         hir::AssocItem::TypeAlias(it) => Some(*it),
                         _ => None,
                     })
-                    .find(|alias| alias.name(sema.db).to_string() == **name_ref.text())
+                    .find(|alias| &alias.name(sema.db).to_string() == name_ref.text())
                 {
                     return Some(NameRefClass::Definition(Definition::ModuleDef(
                         ModuleDef::TypeAlias(ty),
index 877d4f1c79a53de5cf2cb9359c914d1eba173a6c..fd403519875c9cee6f7e39b9be0bbb29f0702412 100644 (file)
@@ -507,7 +507,7 @@ fn new(path: &ast::Path) -> ImportGroup {
             PathSegmentKind::SelfKw => ImportGroup::ThisModule,
             PathSegmentKind::SuperKw => ImportGroup::SuperModule,
             PathSegmentKind::CrateKw => ImportGroup::ThisCrate,
-            PathSegmentKind::Name(name) => match name.text().as_str() {
+            PathSegmentKind::Name(name) => match name.text() {
                 "std" => ImportGroup::Std,
                 "core" => ImportGroup::Std,
                 _ => ImportGroup::ExternCrate,
index 0ecb13a64b0ae44903b25eb29cd0e37d7f5342cd..b9ba0aed5d328e0d1e7373c01929905da811beee 100644 (file)
@@ -228,6 +228,15 @@ fn search_scope(&self, db: &RootDatabase) -> SearchScope {
                             // so do nothing.
                         }
                     }
+                    ModuleSource::BlockExpr(b) => {
+                        if is_first {
+                            let range = Some(b.syntax().text_range());
+                            res.insert(file_id, range);
+                        } else {
+                            // We have already added the enclosing file to the search scope,
+                            // so do nothing.
+                        }
+                    }
                     ModuleSource::SourceFile(_) => {
                         res.insert(file_id, None);
                     }
@@ -257,6 +266,7 @@ fn search_scope(&self, db: &RootDatabase) -> SearchScope {
         let mut res = FxHashMap::default();
         let range = match module_src.value {
             ModuleSource::Module(m) => Some(m.syntax().text_range()),
+            ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()),
             ModuleSource::SourceFile(_) => None,
         };
         res.insert(file_id, range);
index 0aa6a076517e6cfbfb7502398317615d879f90a9..e954bd72e2ad02a057b1cb72b814478091103ea0 100644 (file)
@@ -191,7 +191,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
 
     let def_map = db.crate_def_map(krate);
     let mut files = Vec::new();
-    let mut modules = vec![def_map.root];
+    let mut modules = vec![def_map.root()];
     while let Some(module) = modules.pop() {
         let data = &def_map[module];
         files.extend(data.origin.file_id());
@@ -209,7 +209,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
     query.search(&buf)
 }
 
-pub fn index_resolve(db: &RootDatabase, name: &SmolStr) -> Vec<FileSymbol> {
+pub fn index_resolve(db: &RootDatabase, name: &str) -> Vec<FileSymbol> {
     let mut query = Query::new(name.to_string());
     query.exact();
     query.limit(4);
@@ -409,7 +409,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
     fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
         let name = node.name()?;
         let name_range = name.syntax().text_range();
-        let name = name.text().clone();
+        let name = name.text().into();
         let ptr = SyntaxNodePtr::new(node.syntax());
 
         Some((name, ptr, name_range))
index 51002e7b87d02e69862d106681b98ce6c9719b21..0cdc175be7bd6aaec1a30d43b0f927e84566485b 100644 (file)
@@ -507,7 +507,7 @@ fn to_char(&self) -> Option<char> {
         }
     }
     fn to_text(&self) -> SmolStr {
-        self.token().text().clone()
+        self.token().text().into()
     }
 }
 
@@ -682,10 +682,8 @@ fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) {
             self.text_pos += TextSize::of(text);
         }
 
-        let text = SmolStr::new(self.buf.as_str());
+        self.inner.token(kind, self.buf.as_str());
         self.buf.clear();
-        self.inner.token(kind, text);
-
         // Add whitespace between adjoint puncts
         let next = last.bump();
         if let (
index f7231c2b8eaab89606e1d4500b48ac02d5faa279..cc7da27f7e51bb301da0cb27447251cf5a4937bf 100644 (file)
@@ -14,6 +14,7 @@ once_cell = "1.3.1"
 cfg-if = "1"
 libc = "0.2.73"
 la-arena = { version = "0.2.0", path = "../../lib/arena" }
+countme = { version = "2.0.0-pre.2", features = ["enable"] }
 jemalloc-ctl = { version = "0.3.3", optional = true }
 
 [target.'cfg(target_os = "linux")'.dependencies]
index 8957ea0164342778928b56fd33ef6c9549a3ce22..29d2ed51870ccfe75ef5adf7dd211161556fa74c 100644 (file)
@@ -3,6 +3,7 @@
 use std::{
     cell::RefCell,
     collections::{BTreeMap, HashSet},
+    env,
     io::{stderr, Write},
     sync::{
         atomic::{AtomicBool, Ordering},
@@ -18,7 +19,8 @@
 /// env RA_PROFILE=foo|bar|baz   // enabled only selected entries
 /// env RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10 ms
 pub fn init() {
-    let spec = std::env::var("RA_PROFILE").unwrap_or_default();
+    countme::enable(env::var("RA_COUNT").is_ok());
+    let spec = env::var("RA_PROFILE").unwrap_or_default();
     init_from(&spec);
 }
 
index aa6ccc36c3f3a31305f3710c2c5be11ddd45ff62..79dba47d5e1cbda607477d1ed54915a0af4b38f1 100644 (file)
     stop_watch::{StopWatch, StopWatchSpan},
 };
 
+pub use countme;
+/// Include `_c: Count<Self>` field in important structs to count them.
+///
+/// To view the counts, run with `RA_COUNT=1`. The overhead of disabled count is
+/// almost zero.
+pub use countme::Count;
+
 thread_local!(static IN_SCOPE: RefCell<bool> = RefCell::new(false));
 
 /// Allows to check if the current code is withing some dynamic scope, can be
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
new file mode 100644 (file)
index 0000000..cf32995
--- /dev/null
@@ -0,0 +1,206 @@
+//! Handles build script specific information
+
+use std::{
+    ffi::OsStr,
+    io::BufReader,
+    path::{Path, PathBuf},
+    process::{Command, Stdio},
+};
+
+use anyhow::Result;
+use cargo_metadata::{BuildScript, Message, Package, PackageId};
+use itertools::Itertools;
+use paths::{AbsPath, AbsPathBuf};
+use rustc_hash::FxHashMap;
+use stdx::JodChild;
+
+use crate::{cfg_flag::CfgFlag, CargoConfig};
+
+#[derive(Debug, Clone, Default)]
+pub(crate) struct BuildDataMap {
+    data: FxHashMap<PackageId, BuildData>,
+}
+#[derive(Debug, Clone, Default, PartialEq, Eq)]
+pub struct BuildData {
+    /// List of config flags defined by this package's build script
+    pub cfgs: Vec<CfgFlag>,
+    /// List of cargo-related environment variables with their value
+    ///
+    /// If the package has a build script which defines environment variables,
+    /// they can also be found here.
+    pub envs: Vec<(String, String)>,
+    /// Directory where a build script might place its output
+    pub out_dir: Option<AbsPathBuf>,
+    /// Path to the proc-macro library file if this package exposes proc-macros
+    pub proc_macro_dylib_path: Option<AbsPathBuf>,
+}
+
+impl BuildDataMap {
+    pub(crate) fn new(
+        cargo_toml: &AbsPath,
+        cargo_features: &CargoConfig,
+        packages: &Vec<Package>,
+        progress: &dyn Fn(String),
+    ) -> Result<BuildDataMap> {
+        let mut cmd = Command::new(toolchain::cargo());
+        cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
+            .arg(cargo_toml.as_ref());
+
+        // --all-targets includes tests, benches and examples in addition to the
+        // default lib and bins. This is an independent concept from the --targets
+        // flag below.
+        cmd.arg("--all-targets");
+
+        if let Some(target) = &cargo_features.target {
+            cmd.args(&["--target", target]);
+        }
+
+        if cargo_features.all_features {
+            cmd.arg("--all-features");
+        } else {
+            if cargo_features.no_default_features {
+                // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
+                // https://github.com/oli-obk/cargo_metadata/issues/79
+                cmd.arg("--no-default-features");
+            }
+            if !cargo_features.features.is_empty() {
+                cmd.arg("--features");
+                cmd.arg(cargo_features.features.join(" "));
+            }
+        }
+
+        cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
+
+        let mut child = cmd.spawn().map(JodChild)?;
+        let child_stdout = child.stdout.take().unwrap();
+        let stdout = BufReader::new(child_stdout);
+
+        let mut res = BuildDataMap::default();
+        for message in cargo_metadata::Message::parse_stream(stdout) {
+            if let Ok(message) = message {
+                match message {
+                    Message::BuildScriptExecuted(BuildScript {
+                        package_id,
+                        out_dir,
+                        cfgs,
+                        env,
+                        ..
+                    }) => {
+                        let cfgs = {
+                            let mut acc = Vec::new();
+                            for cfg in cfgs {
+                                match cfg.parse::<CfgFlag>() {
+                                    Ok(it) => acc.push(it),
+                                    Err(err) => {
+                                        anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
+                                    }
+                                };
+                            }
+                            acc
+                        };
+                        let res = res.data.entry(package_id.clone()).or_default();
+                        // cargo_metadata crate returns default (empty) path for
+                        // older cargos, which is not absolute, so work around that.
+                        if out_dir != PathBuf::default() {
+                            let out_dir = AbsPathBuf::assert(out_dir);
+                            res.out_dir = Some(out_dir);
+                            res.cfgs = cfgs;
+                        }
+
+                        res.envs = env;
+                    }
+                    Message::CompilerArtifact(message) => {
+                        progress(format!("metadata {}", message.target.name));
+
+                        if message.target.kind.contains(&"proc-macro".to_string()) {
+                            let package_id = message.package_id;
+                            // Skip rmeta file
+                            if let Some(filename) =
+                                message.filenames.iter().find(|name| is_dylib(name))
+                            {
+                                let filename = AbsPathBuf::assert(filename.clone());
+                                let res = res.data.entry(package_id.clone()).or_default();
+                                res.proc_macro_dylib_path = Some(filename);
+                            }
+                        }
+                    }
+                    Message::CompilerMessage(message) => {
+                        progress(message.target.name.clone());
+                    }
+                    Message::Unknown => (),
+                    Message::BuildFinished(_) => {}
+                    Message::TextLine(_) => {}
+                }
+            }
+        }
+        res.inject_cargo_env(packages);
+        Ok(res)
+    }
+
+    pub(crate) fn with_cargo_env(packages: &Vec<Package>) -> Self {
+        let mut res = Self::default();
+        res.inject_cargo_env(packages);
+        res
+    }
+
+    pub(crate) fn get(&self, id: &PackageId) -> Option<&BuildData> {
+        self.data.get(id)
+    }
+
+    fn inject_cargo_env(&mut self, packages: &Vec<Package>) {
+        for meta_pkg in packages {
+            let resource = self.data.entry(meta_pkg.id.clone()).or_default();
+            inject_cargo_env(meta_pkg, &mut resource.envs);
+
+            if let Some(out_dir) = &resource.out_dir {
+                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
+                if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
+                    resource.envs.push(("OUT_DIR".to_string(), out_dir));
+                }
+            }
+        }
+    }
+}
+
+// FIXME: File a better way to know if it is a dylib
+fn is_dylib(path: &Path) -> bool {
+    match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
+        None => false,
+        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
+    }
+}
+
+/// Recreates the compile-time environment variables that Cargo sets.
+///
+/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
+fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) {
+    // FIXME: Missing variables:
+    // CARGO, CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
+
+    let mut manifest_dir = package.manifest_path.clone();
+    manifest_dir.pop();
+    if let Some(cargo_manifest_dir) = manifest_dir.to_str() {
+        env.push(("CARGO_MANIFEST_DIR".into(), cargo_manifest_dir.into()));
+    }
+
+    env.push(("CARGO_PKG_VERSION".into(), package.version.to_string()));
+    env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string()));
+    env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string()));
+    env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string()));
+
+    let pre = package.version.pre.iter().map(|id| id.to_string()).format(".");
+    env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string()));
+
+    let authors = package.authors.join(";");
+    env.push(("CARGO_PKG_AUTHORS".into(), authors));
+
+    env.push(("CARGO_PKG_NAME".into(), package.name.clone()));
+    env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default()));
+    //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default()));
+    env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default()));
+    env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default()));
+
+    let license_file =
+        package.license_file.as_ref().map(|buf| buf.display().to_string()).unwrap_or_default();
+    env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file));
+}
index c0ed37fc128741d6f0615fdf5b7ed59d3d5b6832..c8a5333c44092fa489bc2a943bc35f744ec99db8 100644 (file)
@@ -1,24 +1,15 @@
 //! FIXME: write short doc here
 
-use std::{
-    convert::TryInto,
-    ffi::OsStr,
-    io::BufReader,
-    ops,
-    path::{Path, PathBuf},
-    process::{Command, Stdio},
-};
+use std::{convert::TryInto, ops, process::Command};
 
 use anyhow::{Context, Result};
 use base_db::Edition;
-use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
-use itertools::Itertools;
+use cargo_metadata::{CargoOpt, MetadataCommand};
 use la_arena::{Arena, Idx};
 use paths::{AbsPath, AbsPathBuf};
 use rustc_hash::FxHashMap;
-use stdx::JodChild;
 
-use crate::cfg_flag::CfgFlag;
+use crate::build_data::{BuildData, BuildDataMap};
 use crate::utf8_stdout;
 
 /// `CargoWorkspace` represents the logical structure of, well, a Cargo
@@ -99,19 +90,12 @@ pub struct PackageData {
     pub dependencies: Vec<PackageDependency>,
     /// Rust edition for this package
     pub edition: Edition,
-    /// List of features to activate
-    pub features: Vec<String>,
-    /// List of config flags defined by this package's build script
-    pub cfgs: Vec<CfgFlag>,
-    /// List of cargo-related environment variables with their value
-    ///
-    /// If the package has a build script which defines environment variables,
-    /// they can also be found here.
-    pub envs: Vec<(String, String)>,
-    /// Directory where a build script might place its output
-    pub out_dir: Option<AbsPathBuf>,
-    /// Path to the proc-macro library file if this package exposes proc-macros
-    pub proc_macro_dylib_path: Option<AbsPathBuf>,
+    /// Features provided by the crate, mapped to the features required by that feature.
+    pub features: FxHashMap<String, Vec<String>>,
+    /// List of features enabled on this package
+    pub active_features: Vec<String>,
+    /// Build script related data for this package
+    pub build_data: BuildData,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
@@ -244,17 +228,11 @@ pub fn from_cargo_metadata(
             )
         })?;
 
-        let mut out_dir_by_id = FxHashMap::default();
-        let mut cfgs = FxHashMap::default();
-        let mut envs = FxHashMap::default();
-        let mut proc_macro_dylib_paths = FxHashMap::default();
-        if config.load_out_dirs_from_check {
-            let resources = load_extern_resources(cargo_toml, config, progress)?;
-            out_dir_by_id = resources.out_dirs;
-            cfgs = resources.cfgs;
-            envs = resources.env;
-            proc_macro_dylib_paths = resources.proc_dylib_paths;
-        }
+        let resources = if config.load_out_dirs_from_check {
+            BuildDataMap::new(cargo_toml, config, &meta.packages, progress)?
+        } else {
+            BuildDataMap::with_cargo_env(&meta.packages)
+        };
 
         let mut pkg_by_id = FxHashMap::default();
         let mut packages = Arena::default();
@@ -265,7 +243,7 @@ pub fn from_cargo_metadata(
         meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
         for meta_pkg in meta.packages {
             let id = meta_pkg.id.clone();
-            inject_cargo_env(&meta_pkg, envs.entry(id).or_default());
+            let build_data = resources.get(&id).cloned().unwrap_or_default();
 
             let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
                 meta_pkg;
@@ -281,11 +259,9 @@ pub fn from_cargo_metadata(
                 is_member,
                 edition,
                 dependencies: Vec::new(),
-                features: Vec::new(),
-                cfgs: cfgs.get(&id).cloned().unwrap_or_default(),
-                envs: envs.get(&id).cloned().unwrap_or_default(),
-                out_dir: out_dir_by_id.get(&id).cloned(),
-                proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(),
+                features: meta_pkg.features.into_iter().collect(),
+                active_features: Vec::new(),
+                build_data,
             });
             let pkg_data = &mut packages[pkg];
             pkg_by_id.insert(id, pkg);
@@ -328,7 +304,7 @@ pub fn from_cargo_metadata(
                 let dep = PackageDependency { name: dep_node.name, pkg };
                 packages[source].dependencies.push(dep);
             }
-            packages[source].features.extend(node.features);
+            packages[source].active_features.extend(node.features);
         }
 
         let workspace_root = AbsPathBuf::assert(meta.workspace_root);
@@ -362,149 +338,3 @@ fn is_unique(&self, name: &str) -> bool {
         self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
     }
 }
-
-#[derive(Debug, Clone, Default)]
-pub(crate) struct ExternResources {
-    out_dirs: FxHashMap<PackageId, AbsPathBuf>,
-    proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
-    cfgs: FxHashMap<PackageId, Vec<CfgFlag>>,
-    env: FxHashMap<PackageId, Vec<(String, String)>>,
-}
-
-pub(crate) fn load_extern_resources(
-    cargo_toml: &Path,
-    cargo_features: &CargoConfig,
-    progress: &dyn Fn(String),
-) -> Result<ExternResources> {
-    let mut cmd = Command::new(toolchain::cargo());
-    cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
-
-    // --all-targets includes tests, benches and examples in addition to the
-    // default lib and bins. This is an independent concept from the --targets
-    // flag below.
-    cmd.arg("--all-targets");
-
-    if let Some(target) = &cargo_features.target {
-        cmd.args(&["--target", target]);
-    }
-
-    if cargo_features.all_features {
-        cmd.arg("--all-features");
-    } else {
-        if cargo_features.no_default_features {
-            // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
-            // https://github.com/oli-obk/cargo_metadata/issues/79
-            cmd.arg("--no-default-features");
-        }
-        if !cargo_features.features.is_empty() {
-            cmd.arg("--features");
-            cmd.arg(cargo_features.features.join(" "));
-        }
-    }
-
-    cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
-
-    let mut child = cmd.spawn().map(JodChild)?;
-    let child_stdout = child.stdout.take().unwrap();
-    let stdout = BufReader::new(child_stdout);
-
-    let mut res = ExternResources::default();
-    for message in cargo_metadata::Message::parse_stream(stdout) {
-        if let Ok(message) = message {
-            match message {
-                Message::BuildScriptExecuted(BuildScript {
-                    package_id,
-                    out_dir,
-                    cfgs,
-                    env,
-                    ..
-                }) => {
-                    let cfgs = {
-                        let mut acc = Vec::new();
-                        for cfg in cfgs {
-                            match cfg.parse::<CfgFlag>() {
-                                Ok(it) => acc.push(it),
-                                Err(err) => {
-                                    anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
-                                }
-                            };
-                        }
-                        acc
-                    };
-                    // cargo_metadata crate returns default (empty) path for
-                    // older cargos, which is not absolute, so work around that.
-                    if out_dir != PathBuf::default() {
-                        let out_dir = AbsPathBuf::assert(out_dir);
-                        res.out_dirs.insert(package_id.clone(), out_dir);
-                        res.cfgs.insert(package_id.clone(), cfgs);
-                    }
-
-                    res.env.insert(package_id, env);
-                }
-                Message::CompilerArtifact(message) => {
-                    progress(format!("metadata {}", message.target.name));
-
-                    if message.target.kind.contains(&"proc-macro".to_string()) {
-                        let package_id = message.package_id;
-                        // Skip rmeta file
-                        if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
-                        {
-                            let filename = AbsPathBuf::assert(filename.clone());
-                            res.proc_dylib_paths.insert(package_id, filename);
-                        }
-                    }
-                }
-                Message::CompilerMessage(message) => {
-                    progress(message.target.name.clone());
-                }
-                Message::Unknown => (),
-                Message::BuildFinished(_) => {}
-                Message::TextLine(_) => {}
-            }
-        }
-    }
-    Ok(res)
-}
-
-// FIXME: File a better way to know if it is a dylib
-fn is_dylib(path: &Path) -> bool {
-    match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
-        None => false,
-        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
-    }
-}
-
-/// Recreates the compile-time environment variables that Cargo sets.
-///
-/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
-fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) {
-    // FIXME: Missing variables:
-    // CARGO, CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
-
-    let mut manifest_dir = package.manifest_path.clone();
-    manifest_dir.pop();
-    if let Some(cargo_manifest_dir) = manifest_dir.to_str() {
-        env.push(("CARGO_MANIFEST_DIR".into(), cargo_manifest_dir.into()));
-    }
-
-    env.push(("CARGO_PKG_VERSION".into(), package.version.to_string()));
-    env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string()));
-    env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string()));
-    env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string()));
-
-    let pre = package.version.pre.iter().map(|id| id.to_string()).format(".");
-    env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string()));
-
-    let authors = package.authors.join(";");
-    env.push(("CARGO_PKG_AUTHORS".into(), authors));
-
-    env.push(("CARGO_PKG_NAME".into(), package.name.clone()));
-    env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default()));
-    //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default()));
-    env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default()));
-    env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default()));
-
-    let license_file =
-        package.license_file.as_ref().map(|buf| buf.display().to_string()).unwrap_or_default();
-    env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file));
-}
index 970a7e1402f6768455ab424ec6cdd78d73b4b23d..525c336e6347e089ac358ef79852ddbc0eaea267 100644 (file)
@@ -6,6 +6,7 @@
 mod sysroot;
 mod workspace;
 mod rustc_cfg;
+mod build_data;
 
 use std::{
     fs::{read_dir, ReadDir},
index 8e0481ae97450a25f2e61325c10e9d837a8f037f..bc5041e5a3d5f46f989f0197d7c65d93d1cc4aa0 100644 (file)
@@ -178,7 +178,7 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                     let pkg_root = cargo[pkg].root().to_path_buf();
 
                     let mut include = vec![pkg_root.clone()];
-                    include.extend(cargo[pkg].out_dir.clone());
+                    include.extend(cargo[pkg].build_data.out_dir.clone());
 
                     let mut exclude = vec![pkg_root.join(".git")];
                     if is_member {
@@ -481,26 +481,24 @@ fn add_target_crate_root(
     let edition = pkg.edition;
     let cfg_options = {
         let mut opts = cfg_options.clone();
-        for feature in pkg.features.iter() {
+        for feature in pkg.active_features.iter() {
             opts.insert_key_value("feature".into(), feature.into());
         }
-        opts.extend(pkg.cfgs.iter().cloned());
+        opts.extend(pkg.build_data.cfgs.iter().cloned());
         opts
     };
 
     let mut env = Env::default();
-    for (k, v) in &pkg.envs {
+    for (k, v) in &pkg.build_data.envs {
         env.set(k, v.clone());
     }
-    if let Some(out_dir) = &pkg.out_dir {
-        // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
-        if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
-            env.set("OUT_DIR", out_dir);
-        }
-    }
 
-    let proc_macro =
-        pkg.proc_macro_dylib_path.as_ref().map(|it| proc_macro_loader(&it)).unwrap_or_default();
+    let proc_macro = pkg
+        .build_data
+        .proc_macro_dylib_path
+        .as_ref()
+        .map(|it| proc_macro_loader(&it))
+        .unwrap_or_default();
 
     let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
     let crate_id = crate_graph.add_crate_root(
index 3cb45b030fd27e742bc06274083703b008f8fc47..268c009424fc3da2d95214eefe2e7a7248154bd0 100644 (file)
@@ -24,7 +24,7 @@ jod-thread = "0.1.0"
 log = "0.4.8"
 lsp-types = { version = "0.86.0", features = ["proposed"] }
 parking_lot = "0.11.0"
-pico-args = "0.3.1"
+pico-args = "0.4.0"
 oorandom = "11.1.2"
 rustc-hash = "1.1.0"
 serde = { version = "1.0.106", features = ["derive"] }
index 0a471154e9c4dd3b0c43de0df28ad87c6d1bb536..7d917946e3327a655087419c31ba57f8d2c8eed1 100644 (file)
@@ -109,7 +109,7 @@ pub(crate) fn parse() -> Result<Args> {
         let mut matches = Arguments::from_env();
 
         if matches.contains("--version") {
-            matches.finish()?;
+            finish_args(matches)?;
             return Ok(Args {
                 verbosity: Verbosity::Normal,
                 log_file: None,
@@ -143,7 +143,7 @@ pub(crate) fn parse() -> Result<Args> {
         let subcommand = match matches.subcommand()? {
             Some(it) => it,
             None => {
-                matches.finish()?;
+                finish_args(matches)?;
                 return Ok(Args { verbosity, log_file, command: Command::RunServer });
             }
         };
@@ -160,7 +160,7 @@ pub(crate) fn parse() -> Result<Args> {
                 load_output_dirs: matches.contains("--load-output-dirs"),
                 with_proc_macro: matches.contains("--with-proc-macro"),
                 path: matches
-                    .free_from_str()?
+                    .opt_free_from_str()?
                     .ok_or_else(|| format_err!("expected positional argument"))?,
             }),
             "analysis-bench" => Command::Bench(BenchCmd {
@@ -187,21 +187,21 @@ pub(crate) fn parse() -> Result<Args> {
                 load_output_dirs: matches.contains("--load-output-dirs"),
                 with_proc_macro: matches.contains("--with-proc-macro"),
                 path: matches
-                    .free_from_str()?
+                    .opt_free_from_str()?
                     .ok_or_else(|| format_err!("expected positional argument"))?,
             }),
             "diagnostics" => Command::Diagnostics {
                 load_output_dirs: matches.contains("--load-output-dirs"),
                 with_proc_macro: matches.contains("--with-proc-macro"),
                 path: matches
-                    .free_from_str()?
+                    .opt_free_from_str()?
                     .ok_or_else(|| format_err!("expected positional argument"))?,
             },
             "proc-macro" => Command::ProcMacro,
             "ssr" => Command::Ssr {
                 rules: {
                     let mut acc = Vec::new();
-                    while let Some(rule) = matches.free_from_str()? {
+                    while let Some(rule) = matches.opt_free_from_str()? {
                         acc.push(rule);
                     }
                     acc
@@ -211,7 +211,7 @@ pub(crate) fn parse() -> Result<Args> {
                 debug_snippet: matches.opt_value_from_str("--debug")?,
                 patterns: {
                     let mut acc = Vec::new();
-                    while let Some(rule) = matches.free_from_str()? {
+                    while let Some(rule) = matches.opt_free_from_str()? {
                         acc.push(rule);
                     }
                     acc
@@ -222,7 +222,14 @@ pub(crate) fn parse() -> Result<Args> {
                 return Ok(Args { verbosity, log_file: None, command: Command::Help });
             }
         };
-        matches.finish()?;
+        finish_args(matches)?;
         Ok(Args { verbosity, log_file, command })
     }
 }
+
+fn finish_args(args: Arguments) -> Result<()> {
+    if !args.finish().is_empty() {
+        bail!("Unused arguments.");
+    }
+    Ok(())
+}
index fd1407e6071af41a304b804884ba82c8be49b43a..66416f709522c09bd346a611a775a34f4151792d 100644 (file)
@@ -2,6 +2,7 @@
 //! errors.
 
 use std::{
+    env,
     path::PathBuf,
     time::{SystemTime, UNIX_EPOCH},
 };
@@ -295,6 +296,10 @@ pub fn run(self, verbosity: Verbosity) -> Result<()> {
             report_metric("total memory", memory.allocated.megabytes() as u64, "MB");
         }
 
+        if env::var("RA_COUNT").is_ok() {
+            eprintln!("{}", profile::countme::get_all());
+        }
+
         if self.memory_usage && verbosity.is_verbose() {
             print_memory_usage(host, vfs);
         }
index 10cbd7eeb95d119497f39829799b6f881494da7d..809452e6d04f97d20671007418c830ffc0229802 100644 (file)
@@ -859,6 +859,23 @@ pub(crate) fn handle_formatting(
         RustfmtConfig::Rustfmt { extra_args } => {
             let mut cmd = process::Command::new(toolchain::rustfmt());
             cmd.args(extra_args);
+            // try to chdir to the file so we can respect `rustfmt.toml`
+            // FIXME: use `rustfmt --config-path` once
+            // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
+            match params.text_document.uri.to_file_path() {
+                Ok(mut path) => {
+                    // pop off file name
+                    if path.pop() && path.is_dir() {
+                        cmd.current_dir(path);
+                    }
+                }
+                Err(_) => {
+                    log::error!(
+                        "Unable to get file path for {}, rustfmt.toml might be ignored",
+                        params.text_document.uri
+                    );
+                }
+            }
             if let Some(&crate_id) = crate_ids.first() {
                 // Assume all crates are in the same edition
                 let edition = snap.analysis.crate_edition(crate_id)?;
index 42d313f918314984bda0c7fd2ea32ee5ac0f5057..df013bae99f1fcf6ab07c8f6319a1cd0d3035f8e 100644 (file)
 use ide_db::base_db::FileRange;
 use rustc_hash::FxHashMap;
 use std::{cell::Cell, iter::Peekable};
-use syntax::ast::{AstNode, AstToken};
 use syntax::{ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken};
+use syntax::{
+    ast::{AstNode, AstToken},
+    SmolStr,
+};
 use test_utils::mark;
 
 // Creates a match error. If we're currently attempting to match some code that we thought we were
@@ -398,11 +401,11 @@ fn attempt_match_record_field_list(
         code: &SyntaxNode,
     ) -> Result<(), MatchFailed> {
         // Build a map keyed by field name.
-        let mut fields_by_name = FxHashMap::default();
+        let mut fields_by_name: FxHashMap<SmolStr, SyntaxNode> = FxHashMap::default();
         for child in code.children() {
             if let Some(record) = ast::RecordExprField::cast(child.clone()) {
                 if let Some(name) = record.field_name() {
-                    fields_by_name.insert(name.text().clone(), child.clone());
+                    fields_by_name.insert(name.text().into(), child.clone());
                 }
             }
         }
@@ -473,9 +476,7 @@ fn attempt_match_token_tree(
                         }
                         SyntaxElement::Node(n) => {
                             if let Some(first_token) = n.first_token() {
-                                if Some(first_token.text().as_str())
-                                    == next_pattern_token.as_deref()
-                                {
+                                if Some(first_token.text()) == next_pattern_token.as_deref() {
                                     if let Some(SyntaxElement::Node(p)) = pattern.next() {
                                         // We have a subtree that starts with the next token in our pattern.
                                         self.attempt_match_token_tree(phase, &p, &n)?;
index 7e7ce37bdc30af7bec076c381b5a778d950fc4e2..06a94a46cd088ac88a6a709a0df27483f6463397 100644 (file)
@@ -173,7 +173,7 @@ fn render_token(&mut self, token: &SyntaxToken) {
                 );
             }
         } else {
-            self.out.push_str(token.text().as_str());
+            self.out.push_str(token.text());
         }
     }
 
index f5ceb572928c51b77f87a4457d04a0cd1c5c8fcb..14e5a3b69debf17ed27499c06e00eed16a4a5b94 100644 (file)
@@ -228,7 +228,7 @@ fn resolve_path(&self, path: &ast::Path) -> Option<hir::PathResolution> {
                 None,
                 |_ty, assoc_item| {
                     let item_name = assoc_item.name(self.scope.db)?;
-                    if item_name.to_string().as_str() == name.text().as_str() {
+                    if item_name.to_string().as_str() == name.text() {
                         Some(hir::PathResolution::AssocItem(assoc_item))
                     } else {
                         None
index 52394b3373cbd7a94ad4255ce2b63278cfc371cc..24298fbfad5960a6d46f110d50fe99ea18558347 100644 (file)
@@ -12,15 +12,12 @@ doctest = false
 
 [dependencies]
 itertools = "0.10.0"
-rowan = "0.10.3"
-rustc_lexer = { version = "697.0.0", package = "rustc-ap-rustc_lexer" }
+rowan = "0.12"
+rustc_lexer = { version = "700.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 arrayvec = "0.5.1"
 once_cell = "1.3.1"
 indexmap = "1.4.0"
-# This crate transitively depends on `smol_str` via `rowan`.
-# ideally, `serde` should be enabled by `rust-analyzer`, but we enable it here
-# to reduce number of compilations
 smol_str = { version = "0.1.15", features = ["serde"] }
 serde = { version = "1.0.106", features = ["derive"] }
 
index 827ae78f95db12a32f559080b145fe1ebf1afb44..2ff92f9f624bc7957e4ee6e317099f8255f8897a 100644 (file)
@@ -4,6 +4,7 @@
     fmt,
     hash::BuildHasherDefault,
     ops::{self, RangeInclusive},
+    ptr,
 };
 
 use indexmap::IndexMap;
@@ -171,7 +172,7 @@ fn syntax_element_eq(lhs: &SyntaxElement, rhs: &SyntaxElement) -> bool {
             && lhs.text_range().len() == rhs.text_range().len()
             && match (&lhs, &rhs) {
                 (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => {
-                    lhs.green() == rhs.green() || lhs.text() == rhs.text()
+                    ptr::eq(lhs.green(), rhs.green()) || lhs.text() == rhs.text()
                 }
                 (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(),
                 _ => false,
@@ -566,7 +567,7 @@ fn rewrite_self(
 
 fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
     match element {
-        NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()),
+        NodeOrToken::Node(it) => NodeOrToken::Node(it.green().to_owned()),
         NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
     }
 }
@@ -624,7 +625,7 @@ fn position_of_child(parent: &SyntaxNode, child: SyntaxElement) -> usize {
 
 fn to_green_element(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
     match element {
-        NodeOrToken::Node(it) => it.green().clone().into(),
+        NodeOrToken::Node(it) => it.green().to_owned().into(),
         NodeOrToken::Token(it) => it.green().clone().into(),
     }
 }
index 83de067d97dece6506cfed3ff4c2b547a6381b0c..a25ff655ecdb65b25be909b0684cb81d898b5c30 100644 (file)
@@ -12,7 +12,7 @@
 
 use crate::{
     syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
-    SmolStr, SyntaxKind,
+    SyntaxKind,
 };
 
 pub use self::{
@@ -54,7 +54,7 @@ fn cast(syntax: SyntaxToken) -> Option<Self>
 
     fn syntax(&self) -> &SyntaxToken;
 
-    fn text(&self) -> &SmolStr {
+    fn text(&self) -> &str {
         self.syntax().text()
     }
 }
index 9ffc3ae110ec91e5de87ed20930e6032798de90e..b755c969288043541129b0a66ba87cdc0058f5da 100644 (file)
@@ -478,7 +478,7 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
 }
 
 fn unroot(n: SyntaxNode) -> SyntaxNode {
-    SyntaxNode::new_root(n.green().clone())
+    SyntaxNode::new_root(n.green().to_owned())
 }
 
 pub mod tokens {
@@ -495,7 +495,7 @@ pub fn single_space() -> SyntaxToken {
             .syntax()
             .descendants_with_tokens()
             .filter_map(|it| it.into_token())
-            .find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ")
+            .find(|it| it.kind() == WHITESPACE && it.text() == " ")
             .unwrap()
     }
 
@@ -523,7 +523,7 @@ pub fn single_newline() -> SyntaxToken {
             .syntax()
             .descendants_with_tokens()
             .filter_map(|it| it.into_token())
-            .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n")
+            .find(|it| it.kind() == WHITESPACE && it.text() == "\n")
             .unwrap()
     }
 
@@ -533,7 +533,7 @@ pub fn blank_line() -> SyntaxToken {
             .syntax()
             .descendants_with_tokens()
             .filter_map(|it| it.into_token())
-            .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n\n")
+            .find(|it| it.kind() == WHITESPACE && it.text() == "\n\n")
             .unwrap()
     }
 
index 738c92a5b5b596f453eae5b847d3c8292f8b62cb..5c8cf900ff13a821f7995bbbcbaf7ea78d826daf 100644 (file)
 };
 
 impl ast::Lifetime {
-    pub fn text(&self) -> &SmolStr {
+    pub fn text(&self) -> &str {
         text_of_first_token(self.syntax())
     }
 }
 
 impl ast::Name {
-    pub fn text(&self) -> &SmolStr {
+    pub fn text(&self) -> &str {
         text_of_first_token(self.syntax())
     }
 }
 
 impl ast::NameRef {
-    pub fn text(&self) -> &SmolStr {
+    pub fn text(&self) -> &str {
         text_of_first_token(self.syntax())
     }
 
@@ -34,7 +34,7 @@ pub fn as_tuple_field(&self) -> Option<usize> {
     }
 }
 
-fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
+fn text_of_first_token(node: &SyntaxNode) -> &str {
     node.green().children().next().and_then(|it| it.into_token()).unwrap().text()
 }
 
@@ -121,7 +121,7 @@ pub fn as_simple_key_value(&self) -> Option<(SmolStr, SmolStr)> {
     pub fn simple_name(&self) -> Option<SmolStr> {
         let path = self.path()?;
         match (path.segment(), path.qualifier()) {
-            (Some(segment), None) => Some(segment.syntax().first_token()?.text().clone()),
+            (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
             _ => None,
         }
     }
index 5e9620a40d65fc6b6a998d34c31e585095c4be5f..5e07ec7d15939f764ef362cba031df640f3f0324 100644 (file)
@@ -41,7 +41,7 @@ pub fn doc_comment(&self) -> Option<&str> {
         match kind {
             CommentKind { shape, doc: Some(_) } => {
                 let prefix = kind.prefix();
-                let text = &self.text().as_str()[prefix.len()..];
+                let text = &self.text()[prefix.len()..];
                 let ws = text.chars().next().filter(|c| c.is_whitespace());
                 let text = ws.map_or(text, |ws| &text[ws.len_utf8()..]);
                 match shape {
@@ -156,13 +156,13 @@ pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
 
     pub fn value(&self) -> Option<Cow<'_, str>> {
         if self.is_raw() {
-            let text = self.text().as_str();
+            let text = self.text();
             let text =
                 &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
             return Some(Cow::Borrowed(text));
         }
 
-        let text = self.text().as_str();
+        let text = self.text();
         let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
 
         let mut buf = String::new();
@@ -190,7 +190,7 @@ pub fn value(&self) -> Option<Cow<'_, str>> {
     }
 
     pub fn quote_offsets(&self) -> Option<QuoteOffsets> {
-        let text = self.text().as_str();
+        let text = self.text();
         let offsets = QuoteOffsets::new(text)?;
         let o = self.syntax().text_range().start();
         let offsets = QuoteOffsets {
@@ -560,7 +560,7 @@ impl HasFormatSpecifier for ast::String {
     fn char_ranges(
         &self,
     ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> {
-        let text = self.text().as_str();
+        let text = self.text();
         let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
         let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();
 
@@ -590,7 +590,7 @@ pub fn radix(&self) -> Radix {
     pub fn value(&self) -> Option<u128> {
         let token = self.syntax();
 
-        let mut text = token.text().as_str();
+        let mut text = token.text();
         if let Some(suffix) = self.suffix() {
             text = &text[..text.len() - suffix.len()]
         }
index ea7482bb12451d5b93816f45118bb6c0cf17a27b..11294c5b22b0204a9ad06707e53f7ce9456442f1 100644 (file)
@@ -56,9 +56,9 @@ macro_rules! eprintln {
 };
 pub use parser::{SyntaxKind, T};
 pub use rowan::{
-    Direction, GreenNode, NodeOrToken, SmolStr, SyntaxText, TextRange, TextSize, TokenAtOffset,
-    WalkEvent,
+    Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent,
 };
+pub use smol_str::SmolStr;
 
 /// `Parse` is the result of the parsing: a syntax tree and a collection of
 /// errors.
index 76f01084cd99c7e6916cf4ecced94851c3dcb86c..3d637bf9174041635aaa962f251fbb0407f21a6d 100644 (file)
@@ -73,8 +73,7 @@ fn reparse_token<'node>(
                 new_text.pop();
             }
 
-            let new_token =
-                GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), new_text.into());
+            let new_token = GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), &new_text);
             Some((
                 prev_token.replace_with(new_token),
                 new_err.into_iter().collect(),
index ce27c3dd96a73df7f17d7e72b1bbe89a22d57546..d5ddc076f3cd74a49a3c9eadca3334cc018cfef6 100644 (file)
@@ -8,7 +8,7 @@
     ast,
     parsing::Token,
     syntax_node::GreenNode,
-    SmolStr, SyntaxError,
+    SyntaxError,
     SyntaxKind::{self, *},
     SyntaxTreeBuilder, TextRange, TextSize,
 };
@@ -135,7 +135,7 @@ fn eat_n_trivias(&mut self, n: usize) {
 
     fn do_token(&mut self, kind: SyntaxKind, len: TextSize, n_tokens: usize) {
         let range = TextRange::at(self.text_pos, len);
-        let text: SmolStr = self.text[range].into();
+        let text = &self.text[range];
         self.text_pos += len;
         self.token_pos += n_tokens;
         self.inner.token(kind, text);
index cc30138faa00e8603a864b35055c15c3e9b36d53..8f643b2284e14d3c2f35ab3c6f1993b9a452bad8 100644 (file)
@@ -8,7 +8,7 @@
 
 use rowan::{GreenNodeBuilder, Language};
 
-use crate::{Parse, SmolStr, SyntaxError, SyntaxKind, TextSize};
+use crate::{Parse, SyntaxError, SyntaxKind, TextSize};
 
 pub(crate) use rowan::{GreenNode, GreenToken, NodeOrToken};
 
@@ -53,7 +53,7 @@ pub fn finish(self) -> Parse<SyntaxNode> {
         Parse::new(green, errors)
     }
 
-    pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
+    pub fn token(&mut self, kind: SyntaxKind, text: &str) {
         let kind = RustLanguage::kind_to_raw(kind);
         self.inner.token(kind, text)
     }
index 7901580ee9d7cf2445f37d976db22b9417e94e95..7694e883432115f9004f5915b42a8917c667c2e2 100644 (file)
@@ -116,7 +116,7 @@ fn unquote(text: &str, prefix_len: usize, end_delimiter: char) -> Option<&str> {
     }
 
     let token = literal.token();
-    let text = token.text().as_str();
+    let text = token.text();
 
     // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205)
     let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
index dd2bfc493d19a440efda535ced9e43f29adc8877..24197b3322741289176e0733d02625c0bf6cf94b 100644 (file)
@@ -251,6 +251,9 @@ RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more tha
 
 In particular, I have `export RA_PROFILE='*>10'` in my shell profile.
 
+We also have a "counting" profiler which counts number of instances of popular structs.
+It is enabled by `RA_COUNT=1`.
+
 To measure time for from-scratch analysis, use something like this:
 
 ```
index 21330948ba5a38ca17834d7cbc994de18d73dee7..428cee3adbe39413881eb90e6c20eb02c949d5d4 100644 (file)
@@ -6,6 +6,9 @@ Our approach to "clean code" is two-fold:
 It is explicitly OK for a reviewer to flag only some nits in the PR, and then send a follow-up cleanup PR for things which are easier to explain by example, cc-ing the original author.
 Sending small cleanup PRs (like renaming a single local variable) is encouraged.
 
+When reviewing pull requests prefer extending this document to leaving
+non-reusable comments on the pull request itself.
+
 # General
 
 ## Scale of Changes
@@ -139,6 +142,17 @@ There are many benefits to this:
 
 Formatting ensures that you can use your editor's "number of selected characters" feature to correlate offsets with test's source code.
 
+## Marked Tests
+
+Use
+[`mark::hit! / mark::check!`](https://github.com/rust-analyzer/rust-analyzer/blob/71fe719dd5247ed8615641d9303d7ca1aa201c2f/crates/test_utils/src/mark.rs)
+when testing specific conditions.
+Do not place several marks into a single test or condition.
+Do not reuse marks between several tests.
+
+**Rationale:** marks provide an easy way to find the canonical test for each bit of code.
+This makes it much easier to understand.
+
 ## Function Preconditions
 
 Express function preconditions in types and force the caller to provide them (rather than checking in callee):
@@ -280,6 +294,9 @@ Prefer `Default` even it has to be implemented manually.
 
 **Rationale:** less typing in the common case, uniformity.
 
+Use `Vec::new` rather than `vec![]`. **Rationale:** uniformity, strength
+reduction.
+
 ## Functions Over Objects
 
 Avoid creating "doer" objects.
@@ -372,6 +389,14 @@ This allows for exceptionally good performance, but leads to increased compile t
 Runtime performance obeys 80%/20% rule -- only a small fraction of code is hot.
 Compile time **does not** obey this rule -- all code has to be compiled.
 
+## Appropriate String Types
+
+When interfacing with OS APIs, use `OsString`, even if the original source of data is utf-8 encoded.
+**Rationale:** cleanly delineates the boundary when the data goes into the OS-land.
+
+Use `AbsPathBuf` and `AbsPath` over `std::Path`.
+**Rationale:** rust-analyzer is a long-lived process which handles several projects at the same time.
+It is important not to leak cwd by accident.
 
 # Premature Pessimization
 
@@ -418,12 +443,44 @@ fn frobnicate(s: &str) {
 **Rationale:** reveals the costs.
 It is also more efficient when the caller already owns the allocation.
 
-## Collection types
+## Collection Types
 
 Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`.
 
 **Rationale:** they use a hasher that's significantly faster and using them consistently will reduce code size by some small amount.
 
+## Avoid Intermediate Collections
+
+When writing a recursive function to compute a sets of things, use an accumulator parameter instead of returning a fresh collection.
+Accumulator goes first in the list of arguments.
+
+```rust
+// GOOD
+pub fn reachable_nodes(node: Node) -> FxHashSet<Node> {
+    let mut res = FxHashSet::default();
+    go(&mut res, node);
+    res
+}
+fn go(acc: &mut FxHashSet<Node>, node: Node) {
+    acc.insert(node);
+    for n in node.neighbors() {
+        go(acc, n);
+    }
+}
+
+// BAD
+pub fn reachable_nodes(node: Node) -> FxHashSet<Node> {
+    let mut res = FxHashSet::default();
+    res.insert(node);
+    for n in node.neighbors() {
+        res.extend(reachable_nodes(n));
+    }
+    res
+}
+```
+
+**Rational:** re-use allocations, accumulator style is more concise for complex cases.
+
 # Style
 
 ## Order of Imports
index 4abc7b05396c985f93e86c84410c16769e4e48d5..4e6b439fd6dc9620af0f47dae7e5953128f0eadd 100644 (file)
@@ -12,7 +12,7 @@ doctest = false
 [dependencies]
 anyhow = "1.0.26"
 flate2 = "1.0"
-pico-args = "0.3.1"
+pico-args = "0.4.0"
 proc-macro2 = "1.0.8"
 quote = "1.0.2"
 ungrammar = "1.9"
index 6e18a50a638164d1e8213f4eab10181b26132852..51f58180c61bfcc31f43922fa3fead56468751ad 100644 (file)
@@ -4,7 +4,7 @@
 
 use crate::{
     codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, Mode, PREAMBLE},
-    project_root, rust_files, Result,
+    project_root, rust_files_in, Result,
 };
 
 pub fn generate_assists_tests(mode: Mode) -> Result<()> {
@@ -32,7 +32,7 @@ struct Assist {
 impl Assist {
     fn collect() -> Result<Vec<Assist>> {
         let mut res = Vec::new();
-        for path in rust_files(&project_root().join("crates/assists/src/handlers")) {
+        for path in rust_files_in(&project_root().join("crates/assists/src/handlers")) {
             collect_file(&mut res, path.as_path())?;
         }
         res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
index 00aaea5b7755b324a156bff3ddc0dc6591aa2051..7c14d4a077d529bccbc9a9149afc8735a9f02cbe 100644 (file)
@@ -27,7 +27,7 @@ struct Diagnostic {
 impl Diagnostic {
     fn collect() -> Result<Vec<Diagnostic>> {
         let mut res = Vec::new();
-        for path in rust_files(&project_root()) {
+        for path in rust_files() {
             collect_file(&mut res, path)?;
         }
         res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
index 065dd33f15486714268397658656d104d940e327..61081063b75e4ef15e87ce1a46aa62b804d80042 100644 (file)
@@ -26,7 +26,7 @@ struct Feature {
 impl Feature {
     fn collect() -> Result<Vec<Feature>> {
         let mut res = Vec::new();
-        for path in rust_files(&project_root()) {
+        for path in rust_files() {
             collect_file(&mut res, path)?;
         }
         res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
index babec2dbd000f66f1f7b51edf67d15f71474b763..16b06b853b83ec68c68762bb0496e7046fbc30f2 100644 (file)
@@ -34,7 +34,11 @@ pub fn project_root() -> PathBuf {
     .to_path_buf()
 }
 
-pub fn rust_files(path: &Path) -> impl Iterator<Item = PathBuf> {
+pub fn rust_files() -> impl Iterator<Item = PathBuf> {
+    rust_files_in(&project_root().join("crates"))
+}
+
+pub fn rust_files_in(path: &Path) -> impl Iterator<Item = PathBuf> {
     let iter = WalkDir::new(path);
     return iter
         .into_iter()
index c3e5c732672503500d55e00c430d84ab2a729551..5a99f4a765162d643beccc7cb32eecb7f21488af 100644 (file)
@@ -10,6 +10,7 @@
 
 use std::env;
 
+use anyhow::bail;
 use codegen::CodegenCmd;
 use pico_args::Arguments;
 use xshell::{cmd, cp, pushd};
@@ -76,7 +77,7 @@ fn main() -> Result<()> {
 
             let client_opt = args.opt_value_from_str("--client")?;
 
-            args.finish()?;
+            finish_args(args)?;
 
             InstallCmd {
                 client: if server { None } else { Some(client_opt.unwrap_or_default()) },
@@ -86,53 +87,53 @@ fn main() -> Result<()> {
         }
         "codegen" => {
             let features = args.contains("--features");
-            args.finish()?;
+            finish_args(args)?;
             CodegenCmd { features }.run()
         }
         "format" => {
-            args.finish()?;
+            finish_args(args)?;
             run_rustfmt(Mode::Overwrite)
         }
         "install-pre-commit-hook" => {
-            args.finish()?;
+            finish_args(args)?;
             pre_commit::install_hook()
         }
         "lint" => {
-            args.finish()?;
+            finish_args(args)?;
             run_clippy()
         }
         "fuzz-tests" => {
-            args.finish()?;
+            finish_args(args)?;
             run_fuzzer()
         }
         "pre-cache" => {
-            args.finish()?;
+            finish_args(args)?;
             PreCacheCmd.run()
         }
         "release" => {
             let dry_run = args.contains("--dry-run");
-            args.finish()?;
+            finish_args(args)?;
             ReleaseCmd { dry_run }.run()
         }
         "promote" => {
             let dry_run = args.contains("--dry-run");
-            args.finish()?;
+            finish_args(args)?;
             PromoteCmd { dry_run }.run()
         }
         "dist" => {
             let nightly = args.contains("--nightly");
             let client_version: Option<String> = args.opt_value_from_str("--client")?;
-            args.finish()?;
+            finish_args(args)?;
             DistCmd { nightly, client_version }.run()
         }
         "metrics" => {
             let dry_run = args.contains("--dry-run");
-            args.finish()?;
+            finish_args(args)?;
             MetricsCmd { dry_run }.run()
         }
         "bb" => {
-            let suffix: String = args.free_from_str()?.unwrap();
-            args.finish()?;
+            let suffix: String = args.free_from_str()?;
+            finish_args(args)?;
             cmd!("cargo build --release").run()?;
             cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", suffix))?;
             Ok(())
@@ -161,3 +162,10 @@ fn main() -> Result<()> {
         }
     }
 }
+
+fn finish_args(args: Arguments) -> Result<()> {
+    if !args.finish().is_empty() {
+        bail!("Unused arguments.");
+    }
+    Ok(())
+}
index 6abad189adfadb1d4df6013606aaedfa9bf41c25..9a6933b094db5b4174fbf9df655a6dda57854646 100644 (file)
@@ -82,7 +82,7 @@ fn check_lsp_extensions_docs() {
 #[test]
 fn rust_files_are_tidy() {
     let mut tidy_docs = TidyDocs::default();
-    for path in rust_files(&project_root().join("crates")) {
+    for path in rust_files() {
         let text = read_file(&path).unwrap();
         check_todo(&path, &text);
         check_dbg(&path, &text);